6.16. kink/NUM¶
6.16.1. type num¶
`num` is a floating point decimal number. A `num` value consists of the following attributes:
• Mantissa: a signed integer obtained by removing the decimal point from the number
• Scale: a nonnegative integer which is the number of digits of the fractional part
The number represented by the `num` value is `Mantissa * (10 ** (-Scale))`. Infinity, NaN, signed zeros are not supported.
Invariants
The following invariants hold on `Mantissa`:
• mantissa_bits = 8192
• max_mantissa = 2**mantissa_bits - 1
• min_mantissa = - (2**mantissa_bits - 1)
• min_mantissa ≦ Mantissa ≦ max_mantissa
and on `Scale`:
• max_scale = 32767
• 0 ≦ Scale ≦ max_scale
With those ranges, `num` values should be able to represent:
• The epoch times of all the `instant` values in nanosecond precision.
• Finite (neither NaN nor ±Infinity) IEEE 754 binary64 and decimal128 values, except for the sign of zeros.
Equality
There are multiple representations for one number. For example, 420 can be represented as (Mantissa=420, Scale=0), (Mantissa=42000, Scale=2), and so on. They are treated as equal by the == (op_eq) operator:
stdout.print_line((420 == 420.00).repr) # => true
Bit operations
Bit operations take only integer `num` values as operands. Nonnegative integer `num` values are treated as if infinite 0 bits precede them. Negative integer `num` values are treated as if in two's complement representation with infinite preceding 1 bits.
stdout.print_line((~ 0b_1010).repr) # => (-11)
stdout.print_line('{%b}'.format(~ 0b_1010)) # => -1011
stdout.print_line('{%b}'.format(~ 0b_1010 & 0b_1111_1111)) # => 11110101
6.16.1.1. Num.mantissa¶
`mantissa` returns `Mantissa` of `Num`.
The result is an int `num`.
Example
stdout.print_line(123.45.mantissa.repr) # => 12345
6.16.1.2. Num.scale¶
`scale` returns `Scale` of `Num`.
The result is an int `num`.
Example
stdout.print_line(123.45.scale.repr) # => 2
6.16.1.3. Num.int?¶
`int?` returns whether `Num` is an integer.
A `num` is an integer when `Scale` is zero.
Example
stdout.print_line(120.int?.repr) # => true
stdout.print_line(120.0.int?.repr) # => false
stdout.print_line(3.14.int?.repr) # => false
6.16.1.4. X + Y¶
`+` operator, or `op_add` method, returns the sum of `X` and `Y`.
`Scale` of the result will be max(X.scale, Y.scale).
Precondition
`Y` must be a `num` value.
Example
:Sum <- 1.234 + 0.12
stdout.print_line(Sum.repr) # => 1.354
stdout.print_line(Sum.scale.repr) # => 3
6.16.1.5. X - Y¶
The binary `-` operator, or `op_sub` method, returns the difference of `X` and `Y`.
`Scale` of the result will be max(X.scale, Y.scale).
Precondition
`Y` must be a `num` value.
Example
:Diff <- 1.234 - 0.12
stdout.print_line(Diff.repr) # => 1.114
stdout.print_line(Diff.scale.repr) # => 3
6.16.1.6. - X¶
The unary `-` operator, or `op_minus` method, flips the sign of `X`.
Example
stdout.print_line((-42).repr) # => (-42)
stdout.print_line((-(-42)).repr) # => 42
stdout.print_line((-0).repr) # => 0
6.16.1.7. X * Y¶
`*` operator, or `op_mul` method, returns the product of `X` and `Y`.
`Scale` of the result will be X.scale + Y.scale.
Precondition
`Y` must be a `num` value.
Example
:Prod <- 1.2 * 3.45
stdout.print_line(Prod.repr) # => 4.140
stdout.print_line(Prod.scale.repr) # => 3
6.16.1.8. X / Y¶
`/` operator, or `op_div` method, makes a `num_div` value of (Dividend=X, Divisor=Y).
See kink/NUM_DIV for how to get a number from the `num_div` value.
Preconditions
`Y` must be a `num` value.
`Y` must not be equal to 0.
Example
stdout.print_line((10 / 3).floor(3).repr) # => 3.333
stdout.print_line((10 / 3).ceil(3).repr) # => 3.334
6.16.1.9. X // Y¶
`//` operator, or `op_intdiv` method, performs integral division, and returns the quotient.
Q = X // Y and R = X % Y will suffice the following conditions.
• Q * Y + R = X
• 0 ≦ R < Y.abs
• Q.scale = 0
• R.scale = max(X.scale Y.scale)
Preconditions
`Y` must be a `num` value.
`Y` must not be equal to 0.
Example
:attempt_intdiv <- {(:X :Y)
:Q = X // Y
:R = X % Y
stdout.print_line('{} // {} = {}, remainder: {}'.format(X.repr Y.repr Q.repr R.repr))
}
attempt_intdiv(10 3)
# => 10 // 3 = 3, remainder: 1
attempt_intdiv(10 (-3))
# => 10 // (-3) = (-3), remainder: 1
attempt_intdiv((-10) 3)
# => (-10) // 3 = (-4), remainder: 2
attempt_intdiv((-10) (-3))
# => (-10) // (-3) = 4, remainder: 2
attempt_intdiv(10 1.5)
# => 10 // 1.5 = 6, remainder: 1.0
attempt_intdiv(5.5 3)
# => 5.5 // 3 = 1, remainder: 2.5
attempt_intdiv(5.5 1.5)
# => 5.5 // 1.5 = 3, remainder: 1.0
6.16.1.10. X % Y¶
`%` operator, or `op_rem` method, performs integral division, and returns the remainder.
See X // Y for details.
Preconditions
`Y` must be a `num` value.
`Y` must not be equal to 0.
6.16.1.11. X | Y¶
`|` operator, or `op_or` method, performs bit OR operation for `X` and `Y`.
The result will be an int `num`.
Preconditions
`X` must be an int `num`.
`Y` must be an int `num`.
6.16.1.12. X ^ Y¶
`^` operator, or `op_xor` method, performs bit XOR operation for `X` and `Y`.
The result will be an int `num`.
Preconditions
`X` must be an int `num`.
`Y` must be an int `num`.
6.16.1.13. X & Y¶
`&` operator, or `op_and` method, performs bit AND operation for `X` and `Y`.
The result will be an int `num`.
Preconditions
`X` must be an int `num`.
`Y` must be an int `num`.
6.16.1.14. ~ Num¶
`~` operator, or `op_not` method, performs bit NOT operation `Num`.
Preconditions
`Num` must be an int `num`.
6.16.1.15. Num << Bit_count¶
`<<` operator, or `op_shl` method, performs bit SHIFT-LEFT operation for `Num`.
If `Bit_count` is positive, `Num` is shifted `Bit_count` bits toward left.
If `Bit_count` is negative, `Num` is shifted (-Bit_count) bits toward right. The least significant (-Bit_count) bits are discarded.
If `Bit_count` is zero, `Num` is returned.
The result will be an int `num`.
Preconditions
`Num` must be an int `num`.
`Bit_count` must be an int `num`.
6.16.1.16. Num >> Bit_count¶
`>>` operator, or `op_shr` method, performs bit SHIFT-RIGHT operation for `Num`.
If `Bit_count` is positive, `Num` is shifted `Bit_count` bits toward right. The least significant `Bit_count` bits are discarded.
If `Bit_count` is negative, `Num` is shited (-Bit_count) bits toward left.
If `Bit_count` is zero, `Num` is returned.
The result will be an int `num`.
Preconditions
`Num` must be an int `num`.
`Bit_count` must be an int `num`.
6.16.1.17. X == Y¶
`==` operator, or `op_eq` method, returns whether the numbers are equal, irrespective of scales of them.
Precondition
`Y` must be a `num` value.
Example
stdout.print_line((42 == 42.000).repr) # => true
stdout.print_line((42 != 42.000).repr) # => false
stdout.print_line((42 == 24).repr) # => false
stdout.print_line((42 != 24).repr) # => true
6.16.1.18. X <= Y¶
`<=` operator, or `op_le` method, returns whether `X` is numerically smaller than or equal to `Y`.
Precondition
`Y` must be a `num` value.
Example
:compare <- {(:X :Y)
stdout.print_line('Lt?={} Le?={} Gt?={} Ge?={}'.format(
(X < Y).repr
(X <= Y).repr
(X > Y).repr
(X >= Y).repr))
}
compare(42 42.000) # => Lt?=false Le?=true Gt?=false Ge?=true
compare(42 84) # => Lt?=true Le?=true Gt?=false Ge?=false
compare(42 21) # => Lt?=false Le?=false Gt?=true Ge?=true
6.16.1.19. Num.round¶
`round` returns a `num_div` value of (Dividend=Num, Divisor=1).
See kink/NUM_DIV for how to get a number from the `num_div` value.
Example
stdout.print_line(1.2345.round.floor(2).repr) # => 1.23
6.16.1.20. Num.abs¶
`abs` returns the absolute value of `Num`
If `Num` is nonnegative, `abs` returns `Num`. If `Num` is negative, `abs` returns (-Num).
Example
stdout.print_line(42.abs.repr) # => 42
stdout.print_line((-42).abs.repr) # => 42
6.16.1.21. Num.times¶
`times` returns an `iter` from 0 (inclusive), to `Num` (exclusive), incrementing 1 for each element.
Preconditions
`Num` must be an int `num`.
`Num` ≧ 0
Example
5.times.each{(:N)
stdout.print_line(N.repr)
}
# Output:
# 0
# 1
# 2
# 3
# 4
6.16.1.22. Num.up¶
`up` returns an `iter` from `Num`, incrementing 1 for each element, while the number is in the range of `num` type.
Example
0.5.up.take_front(5).each{(:N)
stdout.print_line(N.repr)
}
# Output:
# 0.5
# 1.5
# 2.5
# 3.5
# 4.5
6.16.1.23. Num.down¶
`down` returns an `iter` from `Num`, decrementing 1 for each element, while the number is in the range of `num` type.
Example
9.5.down.take_front(5).each{(:N)
stdout.print_line(N.repr)
}
# Output:
# 9.5
# 8.5
# 7.5
# 6.5
# 5.5
6.16.1.24. Num.show(...[$config={}])¶
`show` returns a `str` representation of `Num`, which is intended to be used in UI messages.
`show` passes a `num_show_config` value to $config. For details of the output and the configuration, see type `num_show_config`.
Precondition
$config must be a function which takes a `num_show_config` value.
Examples
stdout.print_line(32767.show) # => 32767
stdout.print_line(32767.show{(:S) S.spec('x') }) # => 7fff
stdout.print_line(32767.show{(:S) S.radix(16) }) # => 7fff
stdout.print_line(32767.show{(:S) S.group_sep(3) }) # => 32,767
`show` is usually called from Str.format:
stdout.print_line('{} {%04x}'.format(42 255))
# => 42 00ff
6.16.1.25. Num.repr¶
`repr` returns a `str` such as "1.2500" or "(-42)".
6.16.2. NUM.new(Mantissa Scale)¶
`new` makes a `num` value with given `Mantissa` and `Scale`.
Preconditions
`Mantissa` must be an int `num`.
`Scale` must be an int `num`.
min_mantissa ≦ Mantissa ≦ max_mantissa
0 ≦ Scale ≦ max_scale
Example
:NUM.require_from('kink/')
stdout.print_line(NUM.new(12345 2).repr) # => 123.45
6.16.3. NUM.is?(Val)¶
`is?` returns whether `Val` is a `num` value.
6.16.4. NUM.mantissa_bits¶
`mantissa_bits` is a constant of an integer `num` value 8192, which is the minimum number of bits which can represent `max_mantissa`.
See the kinkdoc of type `num` for details.
6.16.5. NUM.max_mantissa¶
`max_mantissa` is a constant of an integer `num` value 2**8192 - 1, which is the maximum value of `Mantissa` attribute.
See the kinkdoc of type `num` for details.
6.16.6. NUM.min_mantissa¶
`min_mantissa` is a constant of an integer `num` value -(2**8192 - 1), which is the minimum value of `Mantissa` attribute.
See the kinkdoc of type `num` for details.
6.16.7. NUM.max_scale¶
`max_scale` is a constant of an integer `num` value 32767, which is the maximum value of `Scale` attribute.
See the kinkdoc of type `num` for details.
6.16.8. NUM.parse_int(Str ...[$config={}])¶
`parse_int` parses an int num from `Str`.
Config methods:
• C.radix(Radix): default = 10
• C.on_success($success): default = VAL.identity
• C.on_error($error): default = a thunk which raises an exception
If `Str` is accepted by the syntax, and the value is in the range [min_mantissa, max_mantissa], `parse_int` tail-calls $success with the `num` value of the parsed integer.
If `Str` is not accepted by the syntax, or the value is outside the range [min_mantissa, max_mantissa], `parse_int` tail-calls $error with no arg.
Syntax
The string representation is comprised of the two parts:
• Sign: '-', '+', or empty
• Digits: 1 or more digits of the numeral system of `Radix`.
Digits from 0 to 9 are represented by characters '0' to '9'.
Digits from 10 to 35 are represented by characters either 'A' to 'Z', or 'a' to 'z'.
Preconditions
`Str` must be a `str` value.
`Radix` must be an integer `num` within [2, 36].
$success must take an integer `num`.
$error must be a thunk.
Example
:NUM.require_from('kink/')
stdout.print_line(NUM.parse_int('42').repr) # => 42
stdout.print_line(NUM.parse_int('+42').repr) # => 42
stdout.print_line(NUM.parse_int('-42').repr) # => (-42)
stdout.print_line(NUM.parse_int('ff'){(:C) C.radix(16) }.repr) # => 255
stdout.print_line(NUM.parse_int('fF'){(:C) C.radix(16) }.repr) # => 255
stdout.print_line(NUM.parse_int('-ff'){(:C) C.radix(16) }.repr) # => (-255)
Example
:NUM.require_from('kink/')
NUM.parse_int('42'){(:C)
C.on_success{(:N) stdout.print_line(N.repr) }
C.on_error{ stdout.print_line('not valid') }
}
# => 42
NUM.parse_int('*invalid*'){(:C)
C.on_success{(:N) stdout.print_line(N.repr) }
C.on_error{ stdout.print_line('not valid') }
}
# => not valid
6.16.9. NUM.parse_decimal(Str ...[$config={}])¶
`parse_decimal` parses a decimal number from `Str`.
Config methods:
• C.on_success($success): default = VAL.identity
• C.on_error($error): default = a thunk which raises an exception
`Mantissa` of the parsed number is the integer read from the sign and the digits.
`Scale` of the parsed number is the count of the digits after the decimal point `.`. If `Str` does not have the decimal point, `Scale` is 0.
If `Str` is accepted by the syntax, `Mantissa` is in the range [min_mantissa, max_mantissa], and `Scale` is in the range [0, max_scale], `parse_decimal` tail-calls $success with the parsed `num` value.
If `Str` is not accepted by the syntax, or `Mantissa` or `Scale` are not in the ranges, `parse_decimal` tail-calls $error with no arg.
Syntax
Regex: [+-]?([0-9]+(\.[0-9]*)?|\.[0-9]+)
Preconditions
`Str` must be a `str` value.
$success must take a `num` value.
$error must be a thunk.
Example
:NUM.require_from('kink/')
stdout.print_line(NUM.parse_decimal('1.2300').repr) # => 1.2300
stdout.print_line(NUM.parse_decimal('.387').repr) # => 0.387
stdout.print_line(NUM.parse_decimal('-42.').repr) # => (-42)
stdout.print_line(NUM.parse_decimal('24').repr) # => 24
Example
:NUM.require_from('kink/')
NUM.parse_decimal('3.14'){(:C)
C.on_success{(:N) stdout.print_line(N.repr) }
C.on_error{ stdout.print_line('not valid') }
}
# => 3.14
NUM.parse_decimal('*invalid*'){(:C)
C.on_success{(:N) stdout.print_line(N.repr) }
C.on_error{ stdout.print_line('not valid') }
}
# => not valid
6.16.10. type num_show_config¶
`num_show_config` is a subtype of `show_config` (see kink/STR), which is used to configure the output of Num.show.
Results of Num.show are categorized into two cases.
1) When `Scale` of the `num` is less than or equal to 0
In this case, the output will be “{sign}{grouped_padded_integral_digits}”.
2) When `Scale` of the `num` is greater than 0
In this case the radix must be 10, and the output will be “{sign}{grouped_padded_integral_digits}{decimal_sep}{fractional_digits}”.
Part: {sign}
If the `num` is less than 0, {sign} part is the negative sign mark set by `minus_sign` method.
If the `num` is greater than or equal to 0, {sign} part is the non-negative sign mark set by `plus_sign` method.
Part: {grouped_padded_integral_digits}
Firstly, the integral part of the `num` is converted to digits with the radix set by `radix` method. 0-9 and a-z characters are used as digits.
Secondly, "0" is padded to the left of the digits in order that the number of digits in this part is greater than or equal to the minimum number set by `pad_zero` method.
Finally, the digits are grouped using the group length set by `group_len`, and the grouping separator set by `group_sep` method is inserted between groups.
Part: {decimal_sep}
The mark set by `decimal_sep` method.
Part: {fractional_digits}
The decimal digits of the fractional part of the `num`.
The trailing zeros are not omitted, thus the length of the part is always equal to `Scale` of the `num`.
6.16.10.1. C.locale(Locale)¶
`locale` imports configuration of the specified locale.
`locale` configures `decimal_sep` and `group_sep`.
If `locale` method is not called, Num.show does not use any locale information. So, even if you want to use the runtime default locale, you must call `locale` method explicitly.
Precondition
`Locale` must be a `locale`.
Example
:LOCALE.require_from('kink/')
:Shown <- 3776.24.show{(:S)
S.locale(LOCALE.default)
}
stdout.print_line(Shown)
# Output varies by the runtime default locale
6.16.10.2. C.spec(Spec)¶
`spec` configures the output of Num.show method using `Spec` string.
Preconditions
`Spec` must be a `str` value.
`Spec` must be a conjunction of the following fields.
Plus sign field: "+"
Uses "+" as the non-negative sign.
Grouping field: ","
Uses 3 as the grouping length.
Padding field: "0{Min_int_digits}"
Uses Min_int_digits as the minimum number of digits in the integral part.
{Min_int_digits} must be 1 or more decimal digits which represents a positive integer.
Binary field: "b"
Uses 2 as the radix.
Octal field: "o"
Uses 8 as the radix.
Decimal field: "d"
Uses 10 as the radix.
Hexadecimal field: "x"
Uses 16 as the radix.
Examples
stdout.print_line('{%+04f}'.format(255))
# => +00ff
stdout.print_line('{%,}'.format(12345.6789))
# => 12,345.6789
stdout.print_line('{%b}'.format(42))
# => 101010
stdout.print_line('{%o}'.format(42))
# => 52
stdout.print_line('{%d}'.format(42))
# => 42
stdout.print_line('{%x}'.format(42))
# => 2a
6.16.10.3. C.plus_sign(Plus_sign)¶
`plus_sign` sets `Plus_sign` as the sign of non-negative numbers.
The default value is an empty `str` "".
Precondition
`Plus_sign` must be a `str` value.
Example
stdout.print_line(42.show{(:S)
S.plus_sign('+')
})
# => +42
stdout.print_line(0.show{(:S)
S.plus_sign('+')
})
# => +0
stdout.print_line((-10).show{(:S)
S.plus_sign('+')
})
# => -10
6.16.10.4. C.minus_sign(Minus_sign)¶
`minus_sign` sets `Minus_sign` as the sign of negative numbers.
The default value is "-".
Precondition
`Minus_sign` must be a `str` value.
Example
stdout.print_line((-10).show{(:S)
S.minus_sign('minus ')
})
# => minus 10
stdout.print_line(42.show{(:S)
S.minus_sign('minus ')
})
# => 42
stdout.print_line(0.show{(:S)
S.minus_sign('minus ')
})
# => 0
6.16.10.5. C.group_sep(Group_sep)¶
`group_sep` sets `Group_sep` as the digit grouping separator.
The default value is ",".
Precondition
`Group_sep` must be a `str` value.
Example
stdout.print_line(12345.6789.show{(:S)
S.group_sep('_')
S.group_len(3)
})
# => 12_345.6789
stdout.print_line(37767.show{(:S)
S.group_sep('_')
S.group_len(2)
S.radix(16)
})
# => 7f_ff
6.16.10.6. C.group_len(Group_len)¶
`group_len` sets `Group_len` as the length of digits groups.
The default value is the plus infinity. Thus, by default, the grouping separator is not displayed.
Precondition
Group_len must be an int num bigger than or equal to 1
Example
stdout.print_line(12345.6789.show{(:S)
S.group_len(3)
})
# => 12,345.6789
6.16.10.7. C.decimal_sep(Decimal_sep)¶
`decimal_sep` sets `Decimal_sep` as the separator between the integral part and the fractional part.
Precondition
Decimal_sep must be a str
Example
stdout.print_line(12345.6789.show{(:S)
S.decimal_sep(' . ')
})
# => 12345 . 6789
6.16.10.8. C.pad_zero(Min_int_digits)¶
`pad_zero` sets `Min_int_digits` as the minimum number of the integral part to be output.
The default value is 1.
Precondition
`Min_int_digits` must be an int `num` greater than or equal to 1
Example
stdout.print_line(12345.6789.show{(:S)
S.pad_zero(8)
})
# => 00012345.6789
stdout.print_line((-42).show{(:S)
S.pad_zero(4)
})
# => -0042
stdout.print_line(255.show{(:S)
S.pad_zero(4)
S.radix(16)
})
# => 00ff
6.16.10.9. C.radix(Radix)¶
`radix` sets `Radix` as the radix of the result of `show` method.
The default value is 10.
Preconditions
`Radix` must be an int `num` in the range [2, 36].
If the `num` value which is shown is not an integer, `Radix` must be 10.
Example
stdout.print_line(255.show{(:S)
S.radix(2)
})
# => 11111111
stdout.print_line(255.show{(:S)
S.radix(16)
})
# => ff
stdout.print_line((-255).show{(:S)
S.radix(16)
})
# => -ff