#Num
Status: standard library specification draft.
This chapter defines the numeric APIs of std.num:
- the checked, wrapping, and saturating integer methods, which make overflow explicit;
abs_diff, the distance between two integers;to_u64andfrom_u64, the conversions ofIntegerto and fromu64;- the bit counts
count_ones,leading_zeros, andtrailing_zeros; - the rotations
rotate_leftandrotate_right; is_nanandis_finiteonf32andf64;total_cmp, the total order of floats;to_bitsandfrom_bits, the bit patterns off32andf64;abson the signed integers and the floats, andpowon the integers;trunc,floor,ceil, androundon the floats;sqrton the floats, an intrinsic method;- integer parsing through
Parse, withParseNumberError; - radix text:
to_radixandparse_radixon the integers; - float parsing through
Parse; - fixed-point float text:
to_fixedonf64.
The language tier keeps what the compiler knows by name:
| Item | Why it stays in the language tier |
|---|---|
Num, Integer, and Float |
sealed traits that only the standard library implements (Numeric Traits) |
| the numeric families and casts | Numeric Conversions and Numeric Casts |
| checked operators and their panics | Integer Arithmetic |
Note. No numeric value converts implicitly, by
types.num.no-implicit. Each method below takesotherof its receiver's type, so a value of another width is cast first, as ini64(count).checked_add(total).
#Checked Arithmetic
A checked method returns .None where the operator would panic:
fn total(prices: List[i64]) -> i64?:
let sum: i64 = 0
for price in prices:
sum = sum.checked_add(price)?
.Some(sum)In this chapter, N is any integer type: i8, i16, i32, i64, u8,
u16, u32, u64, or usize.
| Rule | Method | Result |
|---|---|---|
std-num.checked.addchecked_add |
fn checked_add(self, other: N) -> N? |
.Some of the exact sum when N holds it, and .None otherwise |
std-num.checked.subchecked_sub |
fn checked_sub(self, other: N) -> N? |
.Some of the exact difference when N holds it, and .None otherwise |
std-num.checked.mulchecked_mul |
fn checked_mul(self, other: N) -> N? |
.Some of the exact product when N holds it, and .None otherwise |
std-num.checked.divchecked_div |
fn checked_div(self, other: N) -> N? |
.Some(self / other) when other is not zero and N holds the quotient, and .None otherwise |
- std-num.methods.every-width
std.numdeclares the checked, wrapping, saturating,abs_diff, and bit-count methods as inherent methods of every integer type, which need nouse. - std-num.checked.no-panicA checked method never panics.
- std-num.checked.div.cases
checked_divtruncates toward zero, as/does. It returns.Nonefor a zero divisor, and for the minimum value divided by-1.
Note. An unsigned
Nholds no value below zero, so itschecked_subreturns.Nonewheneverotheris greater thanself.
#Wrapping Arithmetic
A wrapping method keeps the low bits of the exact result:
fn next_ticket(counter: i32) -> i32:
counter.wrapping_add(1)| Rule | Method | Result |
|---|---|---|
std-num.wrapping.addwrapping_add |
fn wrapping_add(self, other: N) -> N |
the exact sum, wrapped to N |
std-num.wrapping.subwrapping_sub |
fn wrapping_sub(self, other: N) -> N |
the exact difference, wrapped to N |
std-num.wrapping.mulwrapping_mul |
fn wrapping_mul(self, other: N) -> N |
the exact product, wrapped to N |
- std-num.wrapping.resultA value wrapped to
Nis the low bits of its two's-complement form, read asN, as an integer cast reads them bytypes.cast.wrap. - std-num.wrapping.no-panicA wrapping method never panics. So the largest
i32plus one, wrapped, is the smallesti32, and the smallesti64times-1, wrapped, is the smallesti64.
Note. The compiler supplies intrinsic bodies for wrapping arithmetic and integer bounds. Each wrapping operation uses one native arithmetic instruction, with canonicalization for a narrow result; each bound is a width-correct constant.
#Saturating Arithmetic
A saturating method stops at the nearest bound of N:
fn remaining(stock: u32, sold: u32) -> u32:
stock.saturating_sub(sold)| Rule | Method | Result |
|---|---|---|
std-num.saturating.addsaturating_add |
fn saturating_add(self, other: N) -> N |
the exact sum, saturated to N |
std-num.saturating.subsaturating_sub |
fn saturating_sub(self, other: N) -> N |
the exact difference, saturated to N |
- std-num.saturating.resultA value saturated to
Nis the exact result whenNholds it. Above the range ofNit is the largestN, and below it the smallest. - std-num.saturating.no-panicA saturating method never panics. So a
u32zero minus one, saturated, is zero.
#Absolute Difference
abs_diff returns the distance between two integers:
fn gap(a: i32, b: i32) -> u32:
a.abs_diff(b)| Rule | Method |
|---|---|
std-num.abs-diff.i8On i8 |
fn abs_diff(self, other: i8) -> u8 |
std-num.abs-diff.i16On i16 |
fn abs_diff(self, other: i16) -> u16 |
std-num.abs-diff.i32On i32 |
fn abs_diff(self, other: i32) -> u32 |
std-num.abs-diff.i64On i64 |
fn abs_diff(self, other: i64) -> u64 |
std-num.abs-diff.u8On u8 |
fn abs_diff(self, other: u8) -> u8 |
std-num.abs-diff.u16On u16 |
fn abs_diff(self, other: u16) -> u16 |
std-num.abs-diff.u32On u32 |
fn abs_diff(self, other: u32) -> u32 |
std-num.abs-diff.u64On u64 |
fn abs_diff(self, other: u64) -> u64 |
std-num.abs-diff.usizeOn usize |
fn abs_diff(self, other: usize) -> usize |
- std-num.abs-diff
a.abs_diff(b)returns the distance betweenaandbas the table's result type. That is the receiver's own type when it is unsigned, and otherwise the unsigned type of the same width. - std-num.abs-diff.no-panicIt never panics, since that unsigned type holds every distance, including the one between the minimum and the maximum.
- std-num.abs-diff.unsignedThe result is unsigned, so a signed operand beside it needs an explicit cast. Without one, the operation is an error. Error:
mixed-signedness.
fn close(a: i32, b: i32, limit: i32) -> bool:
a.abs_diff(b) < limit # error: mixed-signednessWhy. The distance between the minimum and the maximum of
i32does not fit ini32, so a signed result would have to panic. Rust'si32::abs_diffreturnsu32for the same reason.
#Conversion To And From u64
Integer declares two conversions, so generic code can move any integer
through u64, as a range over any integer type does:
use std.num.Integer
fn widen[N < Integer](n: N) -> u64:
n.to_u64()
fn narrow[N < Integer](n: u64) -> N:
N::from_u64(n)| Rule | Member | Result |
|---|---|---|
std-num.u64.toto_u64 |
fn to_u64(self) -> u64 |
the value as a u64 |
std-num.u64.fromfrom_u64 |
fn from_u64(n: u64) -> Self |
n as the integer type |
- std-num.u64.members
to_u64andfrom_u64are members of the sealedIntegertrait, implemented for every integer type, son.to_u64()andN::from_u64(n)work on a boundN < Integerand on each concrete integer type. - std-num.u64.checkedBoth are checked in every build, as
trait.num.from-i64-checkedis forfrom_i64.to_u64panics on a negative value, andfrom_u64panics when the integer type cannot holdn. Error:integer-overflow. - std-num.u64.exactA conversion that does not panic is exact: the result is the same number, never a wrapped one. A numeric cast
u64(n)stays the way to wrap. - std-num.u64.same-typeOn
u64, both conversions return their argument. - std-num.u64.costEach conversion is a comparison and a cast, with no text round trip.
Why.
from_i64is already checked, so the new members match it. A wrapping conversion would turn a bad range bound into a silent wrong number.
#Bit Counts
The bit counts read the two's-complement form of an integer:
fn flags_set(flags: u8) -> u32:
flags.count_ones()
fn bit_length(value: u32) -> u32:
32 - value.leading_zeros()
fn drop_flagged(value: u64, flags: u8) -> u64:
value >> flags.count_ones() # a u32 shift count, no cast
fn low_bit(value: u64) -> u32:
value.trailing_zeros() # 8 has three trailing zeros.
fn zero_width() -> u32:
let zero: u16 = 0
zero.trailing_zeros() # 16, the type's bit width.| Rule | Method | Result |
|---|---|---|
std-num.bits.count-ones-u32count_ones |
fn count_ones(self) -> u32 |
the number of 1 bits in self |
std-num.bits.leading-zeros-u32leading_zeros |
fn leading_zeros(self) -> u32 |
the number of 0 bits above the highest 1 bit of self, which is the bit width of N when self is zero |
- std-num.bits.signedA signed value counts its two's-complement bits, so
-1asi8has 8 ones, and a negative value has no leading zeros. - std-num.bits.no-panicA bit count never panics.
- std-num.bits.trailing-zeros-u32
trailing_zeros(self) -> u32returns the number of zero bits below the lowest one bit of its integer receiver. - std-num.bits.trailing-zero-widthFor a zero value,
trailing_zerosreturns the bit width of its integer type.
Note. The compiler supplies intrinsic bit-count bodies using Wasm population-count, leading-zero and trailing-zero instructions. Narrow counts use the declared width, and every result is
u32.
Why.
u32is an accepted shift-count type (expr.op.std.shift-unsigned), so a bit count feeds a shift without a cast. Rust'scount_onesandleading_zerosreturnu32too.
#Floating-Point Classification
is_nan tells NaN apart, since NaN is unequal to itself, and is_finite
tells a NaN or an infinity apart from an ordinary value:
fn usable(ratio: f64) -> bool:
!ratio.is_nan()
fn bounded(ratio: f64) -> bool:
ratio.is_finite()- std-num.is-nan
std.numdeclaresfn is_nan(self) -> boolonf32and onf64. It returnstrueexactly whenselfis a NaN. - std-num.is-finite
std.numdeclaresfn is_finite(self) -> boolonf32and onf64. It returnstrueexactly whenselfis neither a NaN nor an infinity.
#Float Bit Patterns
to_bits reads a float's IEEE 754 bit pattern and from_bits builds a float from one, as Rust's do:
fn is_negative_zero(x: f64) -> bool:
x.to_bits() == 9223372036854775808| Rule | Method | Result |
|---|---|---|
std-num.float-bits.toto_bits |
fn to_bits(self) -> U |
the bit pattern of self, sign bit most significant |
std-num.float-bits.fromfrom_bits |
fn from_bits(bits: U) -> F |
the float whose bit pattern is bits |
In this section, F is f64 with U as u64, or F is f32 with U as u32.
- std-num.float-bits.widthsThe two methods exist on
f32and onf64.from_bitstakes no receiver, so a call readsf64::from_bits(bits). - std-num.float-bits.exact
to_bitsreturns the IEEE 754 binary64 pattern of anf64and the binary32 pattern of anf32. So1.0.to_bits()is4607182418800017408and(-0.0).to_bits()is9223372036854775808. - std-num.float-bits.round-trip
from_bits(x.to_bits())has the bits ofx, andfrom_bits(b).to_bits()isb, for everyb, a NaN's sign and payload included. - std-num.float-bits.nanNeither method canonicalizes a NaN.
types.display.nan-canonicalapplies only before hashing, boundary serialization, andDisplay, so a NaN's sign and payload are visible toto_bitsinside the program. - std-num.float-bits.no-panicNeither method panics.
Why. Rust has the same pair. No float operation reveals the sign of a NaN, so
total_cmpneeds the bits. Each method is one reinterpret instruction on Wasm.
#Float Total Order
Floats are not Ord, since NaN is unordered. total_cmp orders every float, so a list of floats sorts:
fn sorted(times: List[f64]) -> List[f64]:
times.sorted_by(fn(a: f64, b: f64) -> Ordering: a.total_cmp(b))| Rule | Method | Result |
|---|---|---|
std-num.float-total-cmp.ordertotal_cmp |
fn total_cmp(self, other: F) -> Ordering |
the IEEE 754 totalOrder of self and other |
In this section, F is f32 or f64.
- std-num.float-total-cmp.widths
total_cmpexists onf32and onf64, and takesotherof the receiver's type. - std-num.float-total-cmp.sequenceThe order is, from least to greatest: a negative NaN, negative infinity, the negative finite values,
-0.0,0.0, the positive finite values, positive infinity, a positive NaN. A NaN's sign is the sign bit of its bit pattern. - std-num.float-total-cmp.nan-payloadAmong NaNs of one sign, the order follows the payload bits read as an unsigned integer, reversed for a negative NaN. So two NaNs with the same bits are
.Equal. - std-num.float-total-cmp.equal
a.total_cmp(b)is.Equalexactly whenaandbhave the same bit pattern. So-0.0and0.0are not equal here, although==finds them equal. - std-num.float-total-cmp.no-panic
total_cmpnever panics.
Why. This is Rust's
f64::total_cmp. It compares the bit patterns as sign-magnitude integers, which needs no allocation.
#Integer Parsing
text.parse::[T]() reads a decimal integer of type T from text:
use std.num.{ParseNumberError}
fn port(text: string) -> i32:
match text.parse::[i32]():
.Ok(value) => value
.Err(ParseNumberError.OutOfRange) => 65535
.Err(_) => 8080A failed parse reports one of three errors:
pub enum ParseNumberError:
Empty
InvalidDigit(position: usize)
OutOfRange- std-num.parse.error-import
std.numdeclaresParseNumberError; code imports it from that module. - std-num.parse.grammarThe accepted text is an optional
+or-, then one or more decimal digits0to9, and nothing else. - std-num.parse.no-literal-syntaxSo whitespace,
_separators, radix prefixes such as0x, and type suffixes are not accepted. - std-num.parse.valueAccepted text gives
.Okof its digits read in base ten, negated after a-. - std-num.parse.emptyEmpty text gives
.Err(ParseNumberError.Empty). - std-num.parse.orderNon-empty text is read from left to right, and the first error reached is the result.
- std-num.parse.invalid-digit-byteA character that breaks the grammar gives
.Err(ParseNumberError.InvalidDigit(position)).positionis its byte offset into the text. - std-num.parse.end-of-text-bytesText that ends where the grammar needs a digit gives
InvalidDigitat the text's length in bytes, so a lone-or+givesInvalidDigit(1). - std-num.parse.out-of-rangeA digit that takes the value read so far out of the result type's range gives
.Err(ParseNumberError.OutOfRange). - std-num.parse.error-traits
ParseNumberErrorimplementsEqandDisplay. - std-num.parse.unsigned-type-grammarUnsigned integer types read the same grammar without the
-: an optional+, then one or more digits. Every other rule above holds for them, with the result type's range. - std-num.parse.unsigned.minusA
-at the start of the text is an invalid digit, so"-1","-0", and"-"give.Err(ParseNumberError.InvalidDigit(0)). - std-num.parse.usize-type-range
usizeparsing uses the target width bytypes.usize.width; on Wasm32 its range matchesu32.
| Text | text.parse::[i32]() gives |
|---|---|
"42", "+42", "042" |
.Ok(42) |
"-2147483648" |
.Ok of the smallest i32 |
"" |
.Err(Empty) |
" 42" |
.Err(InvalidDigit(0)) |
"-", "+" |
.Err(InvalidDigit(1)) |
"1_000", "0x10" |
.Err(InvalidDigit(1)) |
"2147483648", "99999999999x" |
.Err(OutOfRange) |
| Text | text.parse::[u32]() gives |
|---|---|
"42", "+42" |
.Ok(42) |
"4294967295" |
.Ok of the largest u32 |
"4294967296" |
.Err(OutOfRange) |
"-1", "-0", "-" |
.Err(InvalidDigit(0)) |
"+" |
.Err(InvalidDigit(1)) |
"" |
.Err(Empty) |
Note. Every character before
positionis ASCII, sopositionis also the character's index.
Why. Parsing reads user input, which should not accept source literal syntax. The grammar is Rust's
str::parsefor integers, and Rust's unsigned types reject a leading-as an invalid digit too.
#Parse Impls
Every number type implements Parse, so
"42".parse::[u8]() reads a u8:
fn byte(text: string) -> Result[u8, ParseNumberError]:
text.parse::[u8]()- std-num.parse.trait-impls
i8,i16,i32,i64,u8,u16,u32,u64,usize,f32, andf64each implementParsewithErrorset toParseNumberError. - std-num.parse.integer-grammarInteger
Parseimplementations read the signed or unsigned grammar above within their type's range, with the errors and positions defined there. - std-num.parse.trait-narrowFor
i8,i16,u8, andu16, a value past the type's range gives.Err(ParseNumberError.OutOfRange), so"256".parse::[u8]()and"-129".parse::[i8]()fail. - std-num.parse.float64
f64parsing reads the grammar and values defined by Float Parsing. - std-num.parse.float32
f32parsing reads thef64grammar and errors, then rounds the resulting value tof32.
Why. The text of a number is the same whatever the type that reads it, so each width reuses one grammar. Rust parses
f32text directly, so a few decimals that sit between twof32values may round differently here, since the value is rounded twice.
#Radix Text
to_radix writes an integer in a base from 2 to 36, and parse_radix reads
it back, for hex ids, colors, and file modes:
fn id_text(id: u32) -> string:
id.to_radix(16).pad_start(8, '0')
fn mode(text: string) -> Result[u32, ParseNumberError]:
u32::parse_radix(text, 8)| Rule | Method | Result |
|---|---|---|
std-num.radix.to-radixto_radix |
fn to_radix(self, radix: u32) -> string |
the digits of self in radix |
std-num.radix.parse-radixparse_radix |
fn parse_radix(text: string, radix: u32) -> Result[N, ParseNumberError] |
the integer that text spells in radix, or the first error |
- std-num.radix.widthsBoth exist on every integer type,
i8toi64andu8tou64includingusize, and use that type's range.parse_radixtakes no receiver:i32::parse_radix("ff", 16). - std-num.radix.bad-radixA
radixbelow 2 or above 36 panics withexplicit-panic, in both methods, asstd-text.char.to-digit.radixdoes. - std-num.radix.digitsThe digits are
0to9and thenatoz, sozis 35. - std-num.radix.to-radix.format
to_radixwrites no leading zeros, writes letters in lowercase, writes no prefix such as0x, and writes0for zero. - std-num.radix.to-radix.signA negative value is written as
-and then the digits of its magnitude. The minimum value of a signed type is written in full, soi32minimum in radix 16 is-80000000. - std-num.radix.parse.grammar
parse_radixaccepts an optional+or-, then one or more digits ofradix, and nothing else. A letter digit is accepted in either case. - std-num.radix.parse.no-prefixSo whitespace,
_separators, and prefixes such as0xare not accepted;"0xff"in radix 16 givesInvalidDigit(1). - std-num.radix.parse.unsignedAn unsigned type rejects a leading
-as an invalid digit at position 0, asstd-num.parse.unsigned.minusdoes. - std-num.radix.parse.errorsThe errors, their positions, and their order are those of
std-num.parse.empty,std-num.parse.order,std-num.parse.invalid-digit-byte,std-num.parse.end-of-text-bytes, andstd-num.parse.out-of-range. A character that is not a digit ofradixis an invalid digit. - std-num.radix.round-tripFor every integer
xof typeNand everyradixfrom 2 to 36,N::parse_radix(x.to_radix(radix), radix)gives.Ok(x), the minimum and maximum of each width included. - std-num.radix.cost
to_radixbuilds its text in one buffer, not one string per digit, so its cost is linear in the number of digits.
| Call | Result |
|---|---|
(255).to_radix(16) |
"ff" |
(-255).to_radix(2) |
"-11111111" |
the u64 maximum, to_radix(36) |
"3w5e11264sgsf" |
i32::parse_radix("-FF", 16) |
.Ok(-255) |
i32::parse_radix("12", 2) |
.Err(ParseNumberError.InvalidDigit(1)) |
u8::parse_radix("100", 16) |
.Err(ParseNumberError.OutOfRange) |
u8::parse_radix("-1", 10) |
.Err(ParseNumberError.InvalidDigit(0)) |
i32::parse_radix("1", 37) |
panics |
Why. The names and the 2 to 36 range follow Go's
FormatIntandParseIntand Rust'sfrom_str_radix. Lowercase output, no prefix, an optional sign, and a panic for a bad radix are what those APIs share.
#Float Parsing
text.parse::[f64]() reads a decimal number, or one of the words nan,
inf, and infinity:
fn ratio(text: string) -> f64:
match text.parse::[f64]():
.Ok(value) => value
.Err(_) => 0.0- std-num.parse-f64.float-grammarThe accepted text is an optional
+or-, then a decimal number or a special word. - std-num.parse-f64.decimalA decimal number is digits, then optionally
.and more digits, then an optional exponent. Either side of the.may be empty, but not both. - std-num.parse-f64.exponentAn exponent is
eorE, an optional+or-, and one or more digits. - std-num.parse-f64.specialA special word is
nan,inf, orinfinity, with each letter in either case. - std-num.parse-f64.excludedSo whitespace,
_separators, hex digits, a type suffix, and a,decimal mark are not accepted. - std-num.parse-f64.decimal-valueA decimal number gives
.Okof the value that theparse_f64primitive returns for it. That is the nearestf64, with a tie rounded to the even one. - std-num.parse-f64.signA leading
-negates the value, so-0gives-0.0. A leading+changes nothing. - std-num.parse-f64.rangeSo a value past the finite
f64range gives an infinity, and a value too small for the smallest subnormal gives a zero. Each keeps the text's sign. - std-num.parse-f64.special-value
nangives a NaN, andinfandinfinitygive positive infinity, negated by a leading-. - std-num.parse-f64.emptyEmpty text gives
.Err(ParseNumberError.Empty). - std-num.parse-f64.invalid-digit-byteOther text is read from left to right. The first character that no continuation of the grammar allows gives
.Err(ParseNumberError.InvalidDigit(position)), with its byte offset into the text. - std-num.parse-f64.text-endText that ends before the grammar is complete gives
InvalidDigitat the text's length in characters. So a lone-givesInvalidDigit(1)andingivesInvalidDigit(2). - std-num.parse.float64-no-out-of-range
f64parsing never givesOutOfRange. - std-num.parse.float64-round-tripFor every
f64valuex,x.to_string().parse::[f64]()gives.Okofx: the same value with the same sign, or a NaN whenxis a NaN. - std-num.parse-f64.hook
lib/stdchecks the grammar and reads the sign and the special words itself. It calls the primitive only with an unsigned decimal number. - std-num.parse-f64.hook.totalThe primitive has no error or refusal signal, so
lib/stdgives its value as returned, an infinity or a zero included.
| Text | text.parse::[f64]() gives |
|---|---|
"1.5", "+1.5", "15e-1", "15E-1" |
.Ok(1.5) |
".5", "-.5" |
.Ok(0.5), .Ok(-0.5) |
"5.", "007" |
.Ok(5.0), .Ok(7.0) |
"-0", "-0.0" |
.Ok(-0.0) |
"9007199254740993" |
.Ok(9007199254740992.0), the even one of the two nearest |
"1e400", "-1e400" |
.Ok of positive infinity, and of negative infinity |
"1e-400", "-1e-400" |
.Ok(0.0), .Ok(-0.0) |
"NaN", "nan", "-nan" |
.Ok of a NaN |
"inf", "Infinity", "+INF" |
.Ok of positive infinity |
"-inf", "-Infinity" |
.Ok of negative infinity |
| Text | text.parse::[f64]() gives |
|---|---|
"" |
.Err(Empty) |
" 1.5", "e5" |
.Err(InvalidDigit(0)) |
"1_000.5", "0x1p-3", "1,5", "--1" |
.Err(InvalidDigit(1)) |
".", "+", "-" |
.Err(InvalidDigit(1)), at the text's end |
"1e", "in" |
.Err(InvalidDigit(2)), at the text's end |
"1.5 ", "1.5f", "nana" |
.Err(InvalidDigit(3)) |
"1e+" |
.Err(InvalidDigit(3)), at the text's end |
"+nan5" |
.Err(InvalidDigit(4)) |
"infinit" |
.Err(InvalidDigit(7)), at the text's end |
Why. The grammar is Rust's
str::parse::<f64>, so every text thatDisplaywrites parses back,NaN,inf, and-0.0included. Correct rounding is what Rust andserde_jsongive. It needs big-number arithmetic, so the host supplies it, as it suppliesformat_f64.
Note.
std.jsonchecks its own grammar, that of RFC 8259, before it callstext.parse::[f64](). So JSON text still rejectsNaN,+1,.5, and5., bystd-json.parse.number.grammar.
See also: Strings, Result, JSON Numbers.
#Fixed-Point Text
to_fixed writes an f64 with a fixed number of digits after the
decimal point:
fn price(amount: f64) -> string:
amount.to_fixed(2) # "3.14" for 3.14159
fn seconds(elapsed: f64) -> string:
elapsed.to_fixed(0) # "2" for 2.5, a tie rounded to even| Rule | Method |
|---|---|
std-num.to-fixed.declto_fixed |
pub fn to_fixed(self, digits: usize) -> string, on f64 |
- std-num.to-fixed.valueFor a finite
self,to_fixed(digits)writes the multiple of 10 to the power-digitsnearest the exact value ofself. - std-num.to-fixed.tiesWhen two multiples are equally near, it writes the one whose last digit is even.
- std-num.to-fixed.formThe text is an optional
-, then the integer part in decimal with no leading zero but a lone0. Whendigitsis above 0,.and exactlydigitsdigits follow. - std-num.to-fixed.no-exponentThe text never uses scientific notation, however large or small the value.
- std-num.to-fixed.signThe text starts with
-exactly whenselfhas its sign bit set, so(-0.0).to_fixed(1)is"-0.0"and(-0.001).to_fixed(2)is"-0.00". - std-num.to-fixed.specialA NaN gives
NaN, and the infinities giveinfand-inf, the text thattypes.display.specialgives. - std-num.to-fixed.digits-maxA
digitsabove 100 panics. Panic:explicit-panic. - std-num.to-fixed.hook
lib/stdchecksdigits, and theformat_f64_fixedprimitive writes the text.
| Call | Result |
|---|---|
3.14159.to_fixed(2) |
"3.14" |
0.125.to_fixed(2) |
"0.12", since 0.125 is exact and the tie goes to 2 |
0.1.to_fixed(20) |
"0.10000000000000000555", the exact value of the nearest f64 |
1e21.to_fixed(1) |
"1000000000000000000000.0" |
(-1.5).to_fixed(0) |
"-2" |
7.0.to_fixed(0) |
"7" |
Why. Python's
f"{x:.2f}"rounds the exact binary value, ties to even, and keeps the sign of a negative zero, as these rules do. JavaScript'stoFixedrounds a tie away from zero and drops that sign. Correct rounding of up to 100 digits needs big-number arithmetic, so the host supplies it, as it suppliesformat_f64.
See also: Numeric Display, Float Parsing.
#Rotation
rotate_left and rotate_right turn the bits of an integer around its two
ends, so no bit is lost:
fn next_state(state: u32) -> u32:
state.rotate_left(5) ^ state
fn swap_nibbles(byte: u8) -> u8:
byte.rotate_left(4)| Rule | Method | Result |
|---|---|---|
std-num.rotate.leftrotate_left |
fn rotate_left(self, count: u32) -> N |
the bits of self moved count places toward the high end, with the bits that leave the top entering at the bottom |
std-num.rotate.rightrotate_right |
fn rotate_right(self, count: u32) -> N |
the bits of self moved count places toward the low end, with the bits that leave the bottom entering at the top |
- std-num.rotate.widthsBoth methods exist on every integer type,
i8toi64andu8tou64, and return the receiver's type. - std-num.rotate.count-moduloThe count is taken modulo the bit width of the type, so a count of 0 or of the width returns
self. - std-num.rotate.signedA signed value rotates its two's-complement bits, so the sign bit moves like any other bit.
- std-num.rotate.inverse
x.rotate_right(n)undoesx.rotate_left(n). - std-num.rotate.no-panicA rotation never panics.
| Call | Result |
|---|---|
the u8 value 150 (10010110), rotate_left(1) |
45 (00101101) |
the u8 value 150, rotate_right(3) |
210 (11010010) |
the u8 value 150, rotate_left(9) |
45, since 9 is 1 modulo 8 |
the i8 value -128, rotate_left(1) |
1 |
the i32 value -2, rotate_right(1) |
2147483647 |
the u32 value 0x12345678, rotate_left(8) |
0x34567812 |
Why. The names and the
u32count are Rust'srotate_leftandrotate_right.
#Absolute Value
abs returns the magnitude of a signed integer:
fn distance_from_zero(offset: i32) -> i32:
offset.abs()| Rule | Method |
|---|---|
std-num.abs.i8On i8 |
fn abs(self) -> i8 |
std-num.abs.i16On i16 |
fn abs(self) -> i16 |
std-num.abs.i32On i32 |
fn abs(self) -> i32 |
std-num.abs.i64On i64 |
fn abs(self) -> i64 |
- std-num.abs.value
x.abs()returnsxwhenxis not negative, and-xotherwise. - std-num.abs.widths
absexists on the signed integer types only. An unsigned value is its own magnitude, soabson an unsigned type is an unknown method. Error:unknown-method. - std-num.abs.minThe smallest value of a width has no positive twin, so its
absoverflows exactly as-xdoes, bytypes.arith.checked. It panics in a debug or test build and wraps to the smallest value in a release build. Panic:integer-overflow.
Why. Rust's
i32::absbehaves the same way.abs_diffis the call that never overflows, since its result is unsigned.
#Powers
pow raises an integer to a u32 exponent:
fn cube(side: i32) -> i32:
side.pow(3)| Rule | Method | Result |
|---|---|---|
std-num.pow.declpow |
fn pow(self, exp: u32) -> N |
self multiplied by itself exp times |
- std-num.pow.widths
powexists on every integer type,i8toi64andu8tou64andusize, and returns the receiver's type. - std-num.pow.value
x.pow(n)is the product ofncopies ofx, computed with the*operator ofN. - std-num.pow.zero-exponent
x.pow(0)is 1 for everyx, so0.pow(0)is 1. - std-num.pow.overflowA power that
Ncannot hold overflows as*does. It panics in a debug or test build and wraps in a release build. Panic:integer-overflow. - std-num.pow.no-early-overflowA power that
Nholds never overflows, whatever the exponent's bits. So(-2).pow(7)is the smallesti8.
| Call | Result |
|---|---|
2.pow(10) |
1024 |
the i8 value -2, pow(7) |
-128 |
the u8 value 3, pow(5) |
243 |
the i32 value 5, pow(0) |
1 |
the i8 value -2, pow(8) |
panics in a debug or test build, since 256 is out of range |
Why. Rust's
powtakes au32exponent and overflows like*.
#Float Rounding
trunc, floor, ceil, and round turn a float into a whole-valued
float of the same type. abs returns its magnitude:
fn sale_price(price: f64) -> f64:
price.round()
fn whole_boxes(items: f64, per_box: f64) -> f64:
(items / per_box).ceil()| Rule | Method | Result |
|---|---|---|
std-num.float-absabs |
fn abs(self) -> F |
the magnitude of self |
std-num.float-round.trunctrunc |
fn trunc(self) -> F |
the whole part, toward zero |
std-num.float-round.floorfloor |
fn floor(self) -> F |
the largest whole value not above self |
std-num.float-round.ceilceil |
fn ceil(self) -> F |
the smallest whole value not below self |
std-num.float-round.roundround |
fn round(self) -> F |
the nearest whole value, a tie going away from zero |
In this section, F is f32 or f64.
- std-num.float-round.widthsThe five methods exist on
f32and onf64, and return the receiver's type. - std-num.float-round.exactEach result is exact: the value an infinitely precise computation gives, with no rounding error. So
0.49999999999999994.round()is0.0, and2.5.round()is3.0. - std-num.float-round.unchanged
trunc,floor,ceil, androundreturn a NaN, an infinity, and a finite value of magnitude at least 2 to the power 52 unchanged. Forf32the power is 23. Those values are already whole, and a NaN stays a NaN. - std-num.float-round.zero-signA zero result takes the sign of the value that produced it, and a zero input is returned as is. So
(-0.4).round(),(-0.5).ceil(),(-0.2).trunc(), and(-0.0).floor()are-0.0, and0.4.round()is0.0. - std-num.float-round.tie
roundsends a tie to the whole value of larger magnitude, as Rust'sf64::rounddoes. So(-2.5).round()is-3.0and(-0.5).round()is-1.0. - std-num.float-abs.value
absreturns-xfor a negativex,0.0for either zero, andxotherwise. So(-0.0).abs()is0.0, a NaN stays a NaN, and the infinities give positive infinity. - std-num.float-round.no-panicNone of the five methods panics.
| Call | Result |
|---|---|
2.7.floor(), (-2.1).floor() |
2.0, -3.0 |
2.1.ceil(), (-2.7).ceil() |
3.0, -2.0 |
7.9.trunc(), (-7.9).trunc() |
7.0, -7.0 |
2.4.round(), 2.5.round(), 3.5.round() |
2.0, 3.0, 4.0 |
(-2.5).round() |
-3.0 |
(-0.0).abs() |
0.0 |
Why. The methods and their edge cases are Rust's. Each needs only the saturating
f64toi64cast (types.cast.saturate) below 2 to the power 52, where everyf64fits ani64, so no new primitive is needed.sqrtis not here: no float operation builds it, so it is an intrinsic method (Square Root).
#Square Root
sqrt returns the square root of a float, in the float's own type:
fn distance(dx: f64, dy: f64) -> f64:
(dx * dx + dy * dy).sqrt()| Rule | Method | Result |
|---|---|---|
std-num.float-sqrtsqrt |
fn sqrt(self) -> F |
the square root of self |
In this section, F is f32 or f64.
- std-num.float-sqrt.widths
sqrtexists onf32and onf64, and returns the receiver's type. It is an intrinsic method: its signature is a plain declaration, and the compiler supplies its body. - std-num.float-sqrt.roundedThe result is the square root of
selfcorrectly rounded toF, as IEEE 754squareRootrounds to nearest. So4.0.sqrt()is exactly2.0, and2.0.sqrt()is thef64nearest the square root of 2. - std-num.float-sqrt.zero
sqrtreturns a zero as is, so(-0.0).sqrt()is-0.0and0.0.sqrt()is0.0. - std-num.float-sqrt.specialPositive infinity gives positive infinity, and a NaN gives a NaN.
- std-num.float-sqrt.negativeA value below zero, negative infinity included, gives a NaN. So
(-1.0).sqrt()is a NaN. - std-num.float-sqrt.no-panic
sqrtnever panics.
| Call | Result |
|---|---|
4.0.sqrt(), 2.25.sqrt() |
2.0, 1.5 |
2.0.sqrt() |
1.4142135623730951 |
(-0.0).sqrt() |
-0.0 |
(-1.0).sqrt() |
a NaN |
Why. The method and its edge cases are Rust's
f64::sqrt. One Wasm instruction,f64.sqrtorf32.sqrt, computes the correctly rounded root, while an hd body built from+,*, and/would be slower and could round the last bit wrong.