❄️
【Swift】Double.ulpOfOne
公式の説明
The positive difference between 1.0 and the next greater representable number.
1.0と次に大きい数値の間の正の差。
解説
1.0を表現する Double
を用意( a
)
1.0より大きくて Double
で表現可能な、1.0の隣の値を用意( b
)
b - a
が約2.220446049250313e-16。それを Double.ulpOfOne
とする。
仮数部は
a
が (1)00000略00000
b
が (1)00000略00001
となる。
b
の最後の1の大きさを出力するために、1に2で割る作業を52回すると出来る。
52回2で割るイメージは
100000略00000
010000略00000
001000略00000
略
000000略00010
000000略00001
52がどこから出てきたかというのは、浮動小数点数の仕様から出てきた。
コード
let d = Double.ulpOfOne
print(d) //2.220446049250313e-16
var e = 1.0
for _ in 1...52 {
e /= 2.0
}
print(e) //2.220446049250313e-16
d
と e
が同じになった。
具体的に値を書き出してみる。
import Foundation
var d1 = 1.0
let d1Data = Data(bytes: &d1, count: MemoryLayout.size(ofValue: d1))
d1Data.withUnsafeBytes { pointer in
for i in 0..<d1Data.count {
let byte = pointer[i]
print(String(format: "%x", byte)) //3f f0 00 00 00 00 00 00
}
}
var d2 = 1.0 + Double.ulpOfOne
let d2Data = Data(bytes: &d2, count: MemoryLayout.size(ofValue: d2))
d2Data.withUnsafeBytes { pointer in
for i in 0..<d2Data.count {
let byte = pointer[i]
print(String(format: "%x", byte)) //3f f0 00 00 00 00 00 01
}
}
var d3 = Double.ulpOfOne
let d3Data = Data(bytes: &d3, count: MemoryLayout.size(ofValue: d3))
d3Data.withUnsafeBytes { pointer in
for i in 0..<d3Data.count {
let byte = pointer[i]
print(String(format: "%x", byte)) //3c b0 00 00 00 00 00 00
}
}
d1
と d3
の指数部を比較して
0x3ff - 0x3cb = 0x34 = 0b00110100 = 32 + 16 + 4 = 52
52個のシフトです。
1.0の指数部はほとんどのビットが立っているんですね。
使い方
使い所を予想すると、とんでもなく大きい Double
に Double.ulpOfOne
をかけた結果が100になったら、その数に10や20を足しても意味がない、ということがわかるようになる。予想ですが。
Discussion