// Copyright 2025 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package strconv// Binary to decimal conversion using the Dragonbox algorithm by Junekey Jeon.//// Fixed precision format is not supported by the Dragonbox algorithm// so we continue to use Ryū-printf for this purpose.// See https://github.com/jk-jeon/dragonbox/issues/38 for more details.//// For binary to decimal rounding, uses round to nearest, tie to even.// For decimal to binary rounding, assumes round to nearest, tie to even.//// The original paper by Junekey Jeon can be found at:// https://github.com/jk-jeon/dragonbox/blob/d5dc40ae6a3f1a4559cda816738df2d6255b4e24/other_files/Dragonbox.pdf//// The reference implementation in C++ by Junekey Jeon can be found at:// https://github.com/jk-jeon/dragonbox/blob/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/simple/include/simple_dragonbox.h// dragonboxFtoa computes the decimal significand and exponent// from the binary significand and exponent using the Dragonbox algorithm// and formats the decimal floating point number in d.func dboxFtoa( *decimalSlice, uint64, int, bool, int) {if == 32 {dboxFtoa32(, uint32(), , )return }dboxFtoa64(, , , )}func dboxFtoa64( *decimalSlice, uint64, int, bool) {if == 1<<float64MantBits && ! {// Algorithm 5.6 (page 24). := -mulLog10_2MinusLog10_4Over3() , := dboxPow64(, ) , := dboxRange64(, )if != 2 && != 3 { ++ } := / 10if <= *10 { , := trimZeros()dboxDigits(, , -+1+)return } := dboxRoundUp64(, )if == -77 && %2 != 0 { -- } elseif < { ++ }dboxDigits(, , -)return }// κ = 2 for float64 (section 5.1.3)const ( = 2 = 100// 10**κ = * 10// 10**(κ+1) )// Algorithm 5.2 (page 15). := -mulLog10_2() , := dboxPow64(+, ) , := dboxMulPow64(uint64(*2+1)<<, ) , := /, uint32(%) := dboxDelta64(, )if < {if != 0 || ! || %2 == 0 { , := trimZeros()dboxDigits(, , -+1+)return } -- = * 10 } elseif == { , := dboxParity64(uint64(*2-1), , )if || ( && %2 == 0) { , := trimZeros()dboxDigits(, , -+1+)return } }// Algorithm 5.4 (page 18). := + /2 - /2 , := /, % := 10* + uint64()if == 0 { , := dboxParity64(*2, , )if != ((-/2)%2 != 0) || && %2 != 0 { -- } }dboxDigits(, , -)}// Almost identical to dragonboxFtoa64.// This is kept as a separate copy to minimize runtime overhead.func dboxFtoa32( *decimalSlice, uint32, int, bool) {if == 1<<float32MantBits && ! {// Algorithm 5.6 (page 24). := -mulLog10_2MinusLog10_4Over3() , := dboxPow32(, ) , := dboxRange32(, )if != 2 && != 3 { ++ } := / 10if <= *10 { , := trimZeros(uint64())dboxDigits(, , -+1+)return } := dboxRoundUp32(, )if == -77 && %2 != 0 { -- } elseif < { ++ }dboxDigits(, uint64(), -)return }// κ = 1 for float32 (section 5.1.3)const ( = 1 = 10 = * 10 )// Algorithm 5.2 (page 15). := -mulLog10_2() , := dboxPow32(+, ) , := dboxMulPow32(uint32(*2+1)<<, ) , := /, uint32(%) := dboxDelta32(, )if < {if != 0 || ! || %2 == 0 { , := trimZeros(uint64())dboxDigits(, , -+1+)return } -- = * 10 } elseif == { , := dboxParity32(uint32(*2-1), , )if || ( && %2 == 0) { , := trimZeros(uint64())dboxDigits(, , -+1+)return } }// Algorithm 5.4 (page 18). := + /2 - /2 , := /, % := 10* + uint32()if == 0 { , := dboxParity32(*2, , )if != ((-/2)%2 != 0) || && %2 != 0 { -- } }dboxDigits(, uint64(), -)}// dboxDigits emits decimal digits of mant in d for float64// and adjusts the decimal point based on exp.func dboxDigits( *decimalSlice, uint64, int) { := formatBase10(.d, ) .d = .d[:] .nd = len(.d) .dp = .nd + }// uadd128 returns the full 128 bits of u + n.func uadd128( uint128, uint64) uint128 { := uint64(.Lo + )// Check if lo is wrapped around.if < .Lo { .Hi++ } .Lo = return}// umul64 returns the full 64 bits of x * y.func umul64(, uint32) uint64 {returnuint64() * uint64()}// umul96Upper64 returns the upper 64 bits (out of 96 bits) of x * y.func umul96Upper64( uint32, uint64) uint64 { := uint32( >> 32) := uint32() := umul64(, ) := umul64(, )return + ( >> 32)}// umul96Lower64 returns the lower 64 bits (out of 96 bits) of x * y.func umul96Lower64( uint32, uint64) uint64 {returnuint64(uint64() * )}// umul128Upper64 returns the upper 64 bits (out of 128 bits) of x * y.func umul128Upper64(, uint64) uint64 { := uint32( >> 32) := uint32() := uint32( >> 32) := uint32() := umul64(, ) := umul64(, ) := umul64(, ) := umul64(, ) := ( >> 32) + uint64(uint32()) + uint64(uint32())return + ( >> 32) + ( >> 32) + ( >> 32)}// umul192Upper128 returns the upper 128 bits (out of 192 bits) of x * y.func umul192Upper128( uint64, uint128) uint128 { := umul128(, .Hi) := umul128Upper64(, .Lo)returnuadd128(, )}// umul192Lower128 returns the lower 128 bits (out of 192 bits) of x * y.func umul192Lower128( uint64, uint128) uint128 { := * .Hi := umul128(, .Lo)returnuint128{uint64( + .Hi), .Lo}}// dboxMulPow64 computes x^(i), y^(i), z^(i)// from the precomputed value of φ̃k for float64// and also checks if x^(f), y^(f), z^(f) == 0 (section 5.2.1).func dboxMulPow64( uint64, uint128) ( uint64, bool) { := umul192Upper128(, ) = .Hi = .Lo == 0return}// dboxMulPow32 computes x^(i), y^(i), z^(i)// from the precomputed value of φ̃k for float32// and also checks if x^(f), y^(f), z^(f) == 0 (section 5.2.1).func dboxMulPow32( uint32, uint64) ( uint32, bool) { := umul96Upper64(, ) = uint32( >> 32) = uint32() == 0return}// dboxParity64 computes only the parity of x^(i), y^(i), z^(i)// from the precomputed value of φ̃k for float64// and also checks if x^(f), y^(f), z^(f) = 0 (section 5.2.1).func dboxParity64( uint64, uint128, int) ( bool, bool) { := umul192Lower128(, ) = ((.Hi >> (64 - )) & 1) != 0 = ((uint64(.Hi << )) | (.Lo >> (64 - ))) == 0return}// dboxParity32 computes only the parity of x^(i), y^(i), z^(i)// from the precomputed value of φ̃k for float32// and also checks if x^(f), y^(f), z^(f) = 0 (section 5.2.1).func dboxParity32( uint32, uint64, int) ( bool, bool) { := umul96Lower64(, ) = (( >> (64 - )) & 1) != 0 = uint32(>>(32-)) == 0return}// dboxDelta64 returns δ^(i) from the precomputed value of φ̃k for float64.func dboxDelta64( uint128, int) uint32 {returnuint32(.Hi >> (64 - 1 - ))}// dboxDelta32 returns δ^(i) from the precomputed value of φ̃k for float32.func dboxDelta32( uint64, int) uint32 {returnuint32( >> (64 - 1 - ))}// mulLog10_2MinusLog10_4Over3 computes// ⌊e*log10(2)-log10(4/3)⌋ = ⌊log10(2^e)-log10(4/3)⌋ (section 6.3).func mulLog10_2MinusLog10_4Over3( int) int {// e should be in the range [-2985, 2936].return (*631305 - 261663) >> 21}const ( floatMantBits64 = 52// p = 52 for float64. floatMantBits32 = 23// p = 23 for float32.)// dboxRange64 returns the left and right float64 endpoints.func dboxRange64( uint128, int) (, uint64) { = (.Hi - (.Hi >> (float64MantBits + 2))) >> (64 - float64MantBits - 1 - ) = (.Hi + (.Hi >> (float64MantBits + 1))) >> (64 - float64MantBits - 1 - )return , }// dboxRange32 returns the left and right float32 endpoints.func dboxRange32( uint64, int) (, uint32) { = uint32(( - ( >> (floatMantBits32 + 2))) >> (64 - floatMantBits32 - 1 - )) = uint32(( + ( >> (floatMantBits32 + 1))) >> (64 - floatMantBits32 - 1 - ))return , }// dboxRoundUp64 computes the round up of y (i.e., y^(ru)).func dboxRoundUp64( uint128, int) uint64 {return (.Hi>>(128/2-floatMantBits64-2-) + 1) / 2}// dboxRoundUp32 computes the round up of y (i.e., y^(ru)).func dboxRoundUp32( uint64, int) uint32 {returnuint32(>>(64-floatMantBits32-2-)+1) / 2}// dboxPow64 gets the precomputed value of φ̃̃k for float64.func dboxPow64(, int) ( uint128, int) { , , := pow10()if < 0 || > 55 { .Lo++ } = + - 1return , }// dboxPow32 gets the precomputed value of φ̃̃k for float32.func dboxPow32(, int) ( uint64, int) { , , := pow10()if < 0 || > 27 { .Hi++ } = + - 1return .Hi, }
The pages are generated with Goldsv0.8.3-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.