6.17. kink/NUM

6.17.1. num型

numは十進の浮動小数点数である。

num値は(Mantissa, Scale)から構成される。このnum値は、Mantissa * (10 ** (-Scale))という数値を表現する。

ランタイムは最低でも、Mantissaについては[-(2**95), (2**95) - 1]の範囲を、Scaleについては[-(2**7), (2**7) - 1]の範囲をサポートしなければならない。これらの制限を超えると、オーバーフロー、またはアンダーフローが起きる可能性がある。

注釈: JVM上のKinkランタイムは、Mantissaについて[-(2**2048 - 1), 2**2048 - 1]を、Scaleについて[-(2**15), (2**15)-1]をサポートする。

無限大、NaN, 符号付きゼロはサポートされない。

同値性

ひとつの数値について複数の表現があり得る。たとえば、420は(Mantissa=420, Scale=0), (Mantissa=42000, Scale=2), (Mantissa=42, Scale=-1)などのように表現される。これらの値は、==演算子(op_eqメソッド)で同値として扱われる:

:NUM.require_from('kink/')

stdout.print_line((420 == 420.00).repr) # => true
stdout.print_line((420 == NUM.new(42 (-1))).repr) # => true

ビット操作

ビット操作は整数のnum値だけをオペランドとして取る。非負のnum値は、0のビットが無限に先行しているように扱われる。負のnum値は、1のビットが無限に先行する、2の補数表現として扱われる。

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.17.1.1. Num.mantissa

mantissaはNumのMantissaを戻す。

結果は整数のnum値である。

stdout.print_line(123.45.mantissa.repr)  # => 12345

6.17.1.2. Num.scale

scaleはNumのScaleを戻す。

結果は整数のnum値である。

stdout.print_line(123.45.scale.repr)  # => 2

6.17.1.3. Num.int?

int?はNumが整数かどうかを戻す。

num値が整数であるのは、Scaleが0であるときである。

stdout.print_line(120.int?.repr)    # => true
stdout.print_line(120.0.int?.repr)  # => false
stdout.print_line(3.14.int?.repr)   # => false

6.17.1.4. X + Y

+演算子、またはop_addメソッドは、XとYの和を戻す。

結果のScaleはmax(X.scale, Y.scale)である。

事前条件

Yはnum値でなければならない。

:Sum <- 1.234 + 0.12
stdout.print_line(Sum.repr)       # => 1.354
stdout.print_line(Sum.scale.repr) # => 3

6.17.1.5. X - Y

二項の - 演算子、またはop_subメソッドは、XとYの差を戻す。

結果のScaleはmax(X.scale, Y.scale)である。

事前条件

Yはnum値でなければならない。

:Diff <- 1.234 - 0.12
stdout.print_line(Diff.repr)        # => 1.114
stdout.print_line(Diff.scale.repr)  # => 3

6.17.1.6. - X

単項の - 演算子、またはop_minusメソッドは、Xの符号を反転する。

stdout.print_line((-42).repr)     # => (-42)
stdout.print_line((-(-42)).repr)  # => 42
stdout.print_line((-0).repr)      # => 0

6.17.1.7. X * Y

*演算子、またはop_mulメソッドは、XとYの積を戻す。

結果のScaleは X.scale + Y.scale である。

事前条件

Yはnum値でなければならない。

:Prod <- 1.2 * 3.45
stdout.print_line(Prod.repr)        # => 4.140
stdout.print_line(Prod.scale.repr)  # => 3

6.17.1.8. X / Y

/演算子、またはop_divメソッドは、(Dividend=X, Divisor=Y)からなるnum_div値を戻す。

num_div値からどのように値を取り出すかについては、kink/num_DIVを見よ。

事前条件

Yはnum値でなければならない。

Yは0と等しくてはならない。

stdout.print_line((10 / 3).floor(3).repr) # => 3.333
stdout.print_line((10 / 3).ceil(3).repr)  # => 3.334

6.17.1.9. X // Y

//演算子、またはop_intdivメソッドは、整数除算をおこなって、商を戻す。

Q = X // Y と R = X % Y は、次の条件を満たす。

• Q * Y + R = X

• 0 <= R < Y.abs

• Q.scale = 0

• R.scale = X.scale

上記の規則はR6Rs SchemeのDIVとMODと同等だ。参照:

http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.7.3.1

事前条件

Yはnum値でなければならない。

Yは0と等しくてはならない。

: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.17.1.10. X % Y

%演算子、またはop_remメソッドは、整数除算をおこなって、剰余を戻す。

詳細は X // Y を見よ。

事前条件

Yはnum値でなければならない。

Yは0と等しくてはならない。

6.17.1.11. X | Y

| 演算子、またはop_orメソッドは、XとYに対してビットOR演算をおこなう。

結果は整数のnum値である。

事前条件

Xは整数のnum値でなければならない。

Yは整数のnum値でなければならない。

6.17.1.12. X ^ Y

^ 演算子、またはop_xorメソッドは、XとYに対してビットXOR演算をおこなう。

結果は整数のnum値である。

事前条件

Xは整数のnum値でなければならない。

Yは整数のnum値でなければならない。

6.17.1.13. X & Y

& 演算子、またはop_andメソッドは、XとYに対してビットAND演算をおこなう。

結果は整数のnum値である。

事前条件

Xは整数のnum値でなければならない。

Yは整数のnum値でなければならない。

6.17.1.14. ~ Num

~ 演算子、またはop_notメソッドは、Numに対してビットNOT演算をおこなう。

事前条件

Numは整数のnum値でなければならない。

6.17.1.15. Num << Bit_count

<< 演算子、またはop_shlメソッドは、Numに対してビット左シフト演算をおこなう。

Bit_countが正のとき、NumはBit_countビットだけ左にシフトされる。

Bit_countが負のとき、Numは(-Bit_count)ビットだけ右にシフトされる。下位(-Bit_count)ビットは捨てられる。

Bit_countが0のときは、Numが戻る。

結果は整数のnum値である。

事前条件

Numは整数のnum値でなければならない。

Bit_countは整数のnum値でなければならない。

6.17.1.16. Num >> Bit_count

>> 演算子、またはop_shrメソッドは、Numに対してビット右シフト演算をおこなう。

Bit_countが正のとき、NumはBit_countビットだけ右にシフトされる。下位Bit_countビットは捨てられる。

Bit_countが負のとき、Numは(-Bit_count)ビットだけ左にシフトされる。

Bit_countが0のときは、Numが戻る。

結果は整数のnum値である。

事前条件

Numは整数のnum値でなければならない。

Bit_countは整数のnum値でなければならない。

6.17.1.17. X == Y

== 演算子、またはop_eqメソッドは、ふたつの数値が、Scaleを無視して等しいかどうかを戻す。

事前条件

Yはnum値でなければならない。

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.17.1.18. X <= Y

<= 演算子、またはop_leメソッドは、Xが数値的にY以下かどうかを戻す。

事前条件

Yはnum値でなければならない。

: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.17.1.19. Num.round

roundは、(Dividend=Num, Divisor=1)からなるnum_div値を戻す。

num_div値からどのように値を取り出すかについては、kink/num_DIVを見よ。

stdout.print_line(1.2345.round.floor(2).repr) # => 1.23

6.17.1.20. Num.abs

absはNumの絶対値を戻す。

Numが非負の場合、absはNumを戻す。Numが負の場合、absは(-Num)を戻す。

stdout.print_line(42.abs.repr)    # => 42
stdout.print_line((-42).abs.repr) # => 42

6.17.1.21. Num.times

timesは0から(包含的)、Numまで(排除的)へ、要素ごとに1ずつ増えるiterを戻す。

事前条件

Numは整数のnum値でなければならない。

Numは0以上でなければならない。

5.times.each{(:N)
  stdout.print_line(N.repr)
}
# Output:
#   0
#   1
#   2
#   3
#   4

6.17.1.22. Num.up

upはNumから始まって、要素ごとに1ずつ増える無限長のiterを戻す。

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.17.1.23. Num.down

downはNumから始まって、要素ごとに1ずつ減る無限長のiterを戻す。

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.17.1.24. Num.show(...[$show_config={}])

showはNumの文字列表現をstr値として戻す。結果はUIメッセージに使うことが意図される。

省略可能パラメータ$show_configは、num_show_config型の値を取る関数を引数に取る。showはnum_show_config値を渡して$show_configを呼び出す。

例:

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は通常Str.formatから呼ばれる。

例:

stdout.print_line('{} {%04x}'.format(42 255))
# => 42 00ff

出力と設定の詳細についてはnum_show_config型を見よ。

6.17.1.25. Num.repr

reprは"1.2500", "(-42)", "(num mantissa=1 scale=-10)"のような文字列を戻す。

6.17.2. NUM.is?(Val)

is?はValがnum値であるかどうかを戻す。

6.17.3. NUM.new(Mantissa Scale)

newはMantissaとScaleから新しいnum値を作る

事前条件

Mantissaは整数のnum値でなければならない。

Scaleは整数のnum値でなければならない。

MantissaとScaleはランタイムの制限を超えてはならない。

:NUM.require_from('kink/')

stdout.print_line(NUM.new(12345 2).repr)  # => 123.45

6.17.4. NUM.parse_int(Str ...[$config={}])

parse_intはStrをパースして整数のnum値を戻す。

コンフィグメソッド:

• C.radix(Radix): default = 10

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = 例外を投げる関数

Strが文法に受け入れられる場合、parse_intはパースされた整数のnum値を渡して$successを末尾呼び出しする。

Strが文法に受け入れられない場合、parse_intは$errorを引数なしで末尾呼び出しする。

文法

文字列表現は次のふたつの部分からなる。

• Sign: '-', '+', または空

• Digits: 1桁以上のRadix進法の桁

0から9の桁は、'0'から'9'までの文字で表される。

10から35の桁は、'A'から'Z'、または'a'から'z'までの文字で表される。

事前条件

Strはstr値でなければならない。

Radixは[2, 36]の範囲の整数のnum値でなければならない。

$successは整数のnum値を取らなければならない。

$errorは引数を取らない関数でなければならない。

: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)

: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.17.5. NUM.parse_decimal(Str ...[$config={}])

parse_decimalはStrから十進数をパースする。

コンフィグメソッド:

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = 例外を投げる関数

Strが文法に受け入れられる場合、parse_decimalはnum値を渡して$successを末尾呼び出しする。 `.` のあとの桁の個数が、結果のnum値のScaleになる。

Strが文法に受け入れられない場合、parse_decimalは引数なしで$errorを末尾呼び出しする。

文法

正規表現: [+-]?([0-9]+(\.[0-9]*)?|\.[0-9]+)

事前条件

Strはstr値でなければならない。

$successはnum値を取らなければならない。

$errorは引数を取らない関数でなければならない。

: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)

: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.17.6. num_show_config型

num_show_configはshow_configの部分型(kink/STRを見よ)である。Num.showの出力を設定するのに使われる。

Num.showの結果はふたつのケースに分けられる。

1) num値のScaleが0以下の場合

この場合、出力は “{sign}{grouped_padded_integral_digits}” の形式になる。

2) num値のScaleが0より大きい場合

この場合、基数は10でなければならない。出力は “{sign}{grouped_padded_integral_digits}{decimal_sep}{fractional_digits}” の形式になる。

{sign} の部分

num値が0より小さいとき、{sign}の部分はminus_signメソッドで指定された負の符号になる。

num値が0以上のとき、{sign}の部分はplus_signメソッドで指定された非負の符号になる。

{grouped_padded_integral_digits} の部分

まず、num値の整数部分がradixで指定された基数で、0-9, a-zの桁に変換される。

つぎに、この部分の桁数が、pad_zeroメソッドで指定された最小値よりも少なくならないよう、桁の左側に0が埋められる。

最後に、group_lenで指定されたグループ長によって、桁がグループ化される。グループそれぞれの間には、group_sepで指定された区切り記号が挿入される。

{decimal_sep} の部分

decimal_sepメソッドで指定された記号。

{fractional_digits} の部分

num値の小数部分の十進の桁。

末尾の0は省略されない。したがって、この部分の長さは常にnum値のScaleと等しくなる。

6.17.6.1. C.locale(Locale)

localeメソッドを呼ぶと、指定されたロケールの設定を読み込む。

localeメソッドはdecimal_sepとgroup_sepを設定する。

localeが呼ばれなければ、Num.showはどのロケールの情報も使わない。したがって、ランタイムのデフォルトのロケールを使いたい場合でも、明示的にlocaleメソッドを呼ぶ必要がある。

事前条件

Localeはlocale型の値でなければならない。

: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.17.6.2. C.spec(Spec)

specはNum.showの出力を、Spec文字列で指定する。

事前条件

Specはstr値でなければならない。

Specは以下に述べるフィールドをつなげたものでなければならない。

正の符号のフィールド: "+"

"+" を非負の符号として使う。

グルーピングフィールド: ","

グループ長として3を使う。

パディングフィールド: "0{Min_int_digits}"

Min_int_digitsを整数部分の最低限の桁数として使う。

{Min_int_digits}は1つ以上の十進の桁でなければならない。

二進フィールド: "b"

基数として2を使う。

八進フィールド: "o"

基数として8を使う。

十進フィールド: "d"

基数として10を使う。

十六進フィールド: "x"

基数として16を使う

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.17.6.3. C.plus_sign(Plus_sign)

plus_signはPlus_signを非負の数の符号として設定する。

デフォルト値は空の文字列 "" である。

事前条件

Plus_signはstr値でなければならない。

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.17.6.4. C.minus_sign(Minus_sign)

minus_signはMinus_signを負の数の符号として設定する。

デフォルト値は "-" である。

事前条件

Minus_signはstr値でなければならない。

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.17.6.5. C.group_sep(Group_sep)

group_sepはGroup_sepをグループ区切りとして設定する。

デフォルト値は "," である。

事前条件

Group_sepはstr値でなければならない。

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.17.6.6. C.group_len(Group_len)

group_lenはGroup_lenを桁グループの長さとして設定する。

デフォルト値は正の無限大である。つまり、デフォルトではグループ区切りは使われない。

事前条件

Group_lenは1以上の整数のnum値でなければならない。

stdout.print_line(12345.6789.show{(:S)
  S.group_len(3)
})
# => 12,345.6789

6.17.6.7. C.decimal_sep(Decimal_sep)

decimal_sepはDecimal_sepを、整数部と小数部を区切る記号として設定する。

事前条件

Decimal_sepはstr値でなければならない。

stdout.print_line(12345.6789.show{(:S)
  S.decimal_sep(' . ')
})
# => 12345 . 6789

6.17.6.8. C.pad_zero(Min_int_digits)

pad_zeroはMin_int_digitsを、出力される整数部分の桁数の最小値として設定する。

デフォルト値は1である。

事前条件

Min_int_digitsは1以上の整数のnum値でなければならない。

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.17.6.9. C.radix(Radix)

radixはRadixをshowメソッドの結果の基数として設定する。

デフォルト値は10である。

事前条件

Radixは[2, 36]の範囲内の整数のnum値でなければならない。

showメソッドを呼び出す対象のnum値が整数でないとき、Radixは10でなければならない。

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