mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* SR-106: New floating-point `description` implementation
This replaces the current implementation of `description` and
`debugDescription` for the standard floating-point types with a new
formatting routine based on a variation of Florian Loitsch' Grisu2
algorithm with changes suggested by Andrysco, Jhala, and Lerner's 2016
paper describing Errol3.
Unlike the earlier code based on `sprintf` with a fixed number of
digits, this version always chooses the optimal number of digits. As
such, we can now use the exact same output for both `description` and
`debugDescription` (except of course that `debugDescription` provides
full detail for NaNs).
The implementation has been extensively commented; people familiar with
Grisu-style algorithms should find the code easy to understand.
This implementation is:
* Fast. It uses only fixed-width integer arithmetic and has constant
memory and time requirements.
* Simple. It is only a little more complex than Loitsch' original
implementation of Grisu2. The digit decomposition logic for double is
less than 300 lines of standard C (half of which is common arithmetic
support routines).
* Always Accurate. Converting the decimal form back to binary (using an
accurate algorithm such as Clinger's) will always yield exactly the
original binary value. For the IEEE 754 formats, the round-trip will
produce exactly the same bit pattern in memory. This is an essential
requirement for JSON serialization, debugging, and logging.
* Always Short. This always selects an accurate result with the minimum
number of decimal digits. (So that `1.0 / 10.0` will always print
`0.1`.)
* Always Close. Among all accurate, short results, this always chooses
the result that is closest to the exact floating-point value. (In case
of an exact tie, it rounds the last digit even.)
This resolves SR-106 and related issues that have complained
about the floating-point `description` properties being inexact.
* Remove duplicate infinity handling
* Use defined(__SIZEOF_INT128__) to detect uint128_t support
* Separate `extracting` the integer part from `clearing` the integer part
The previous code was unnecessarily obfuscated by the attempt to combine
these two operations.
* Use `UINT32_MAX` to mask off 32 bits of a larger integer
* Correct the expected NaN results for 32-bit i386
* Make the C++ exceptions here consistent
Adding a C source file somehow exposed an issue in an unrelated C++ file.
Thanks to Joe Groff for the fix.
* Rename SwiftDtoa to ".cpp"
Having a C file in stdlib/public/runtime causes strange
build failures on Linux in unrelated C++ files.
As a workaround, rename SwiftDtoa.c to .cpp to see
if that avoids the problems.
* Revert "Make the C++ exceptions here consistent"
This reverts commit 6cd5c20566.
839 lines
34 KiB
Plaintext
839 lines
34 KiB
Plaintext
// RUN: %empty-directory(%t)
|
|
// RUN: %gyb %s -o %t/FloatingPointPrinting.swift
|
|
// RUN: %line-directive %t/FloatingPointPrinting.swift -- %target-build-swift %t/FloatingPointPrinting.swift -o %t/main.out
|
|
// RUN: %line-directive %t/FloatingPointPrinting.swift -- %target-run %t/main.out
|
|
// RUN: %line-directive %t/FloatingPointPrinting.swift -- %target-run %t/main.out --locale ru_RU.UTF-8
|
|
// REQUIRES: executable_test
|
|
|
|
%{
|
|
from gyb_stdlib_unittest_support import TRACE, stackTrace, trace
|
|
}%
|
|
|
|
import StdlibUnittest
|
|
#if os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
|
|
import Glibc
|
|
#else
|
|
import Darwin
|
|
#endif
|
|
|
|
// It takes about an hour to run the Float formatter over all 2 billion
|
|
// positive values to verify that it's correct. Of course, we don't want
|
|
// to do that for the Swift standard library.
|
|
//
|
|
// Instead, I ran the same test against a version of the Float formatter that
|
|
// used only 45 bits precision (instead of 59) and collected all the values that
|
|
// failed with that reduced precision. This should catch any future mistakes
|
|
// that inadvertently damage the accuracy of the formatter's internal
|
|
// calculations.
|
|
fileprivate let generatedCases_Float: [(Float, String)] = [
|
|
(0x1.ab510cp-126, "1.9621416e-38"),
|
|
(0x1.b2171p-126 , "1.993244e-38"),
|
|
(0x1.b2171p-125 , "3.986488e-38"),
|
|
(0x1.b2171p-124 , "7.972976e-38"),
|
|
(0x1.1c01b4p-122, "2.0865513e-37"),
|
|
(0x1.e41e3p-122 , "3.5567368e-37"),
|
|
(0x1.070866p-120, "7.7298394e-37"),
|
|
(0x1.0a4cap-120 , "7.825834e-37"),
|
|
(0x1.2e92dep-118, "3.5567368e-36"),
|
|
(0x1.225e0ap-117, "6.826503e-36"),
|
|
(0x1.288e84p-117, "6.9720146e-36"),
|
|
(0x1.732b26p-117, "8.726131e-36"),
|
|
(0x1.ab8e9cp-117, "1.0051818e-35"),
|
|
(0x1.ea228ap-117, "1.15230165e-35"),
|
|
(0x1.fc5bb8p-117, "1.1951446e-35"),
|
|
(0x1.9cfcbcp-113, "1.5534853e-34"),
|
|
(0x1.e11c02p-112, "3.619465e-34"),
|
|
(0x1.56f96cp-110, "1.0321008e-33"),
|
|
(0x1.9d393ep-110, "1.2434995e-33"),
|
|
(0x1.e11c02p-109, "2.895572e-33"),
|
|
(0x1.0186b2p-107, "6.199717e-33"),
|
|
(0x1.e11c02p-107, "1.1582288e-32"),
|
|
(0x1.87454cp-106, "1.8838998e-32"),
|
|
(0x1.a337f6p-105, "4.0369282e-32"),
|
|
(0x1.71515ap-105, "3.556401e-32"),
|
|
(0x1.5d594ep-101, "5.382571e-31"),
|
|
(0x1.0d7e9ep-100, "8.304443e-31"),
|
|
(0x1.5d594ep-100, "1.0765142e-30"),
|
|
(0x1.fd0eacp-99 , "3.137308e-30"),
|
|
(0x1.356bf6p-98 , "3.813917e-30"),
|
|
(0x1.356bf6p-97 , "7.627834e-30"),
|
|
(0x1.6256f8p-97 , "8.7351485e-30"),
|
|
(0x1.6c524ep-97 , "8.9812184e-30"),
|
|
(0x1.993d5p-97 , "1.0088533e-29"),
|
|
(0x1.c62854p-97 , "1.11958476e-29"),
|
|
(0x1.fd0eacp-97 , "1.2549232e-29"),
|
|
(0x1.e41a56p-96 , "2.3868115e-29"),
|
|
(0x1.266f8p-94 , "5.806717e-29"),
|
|
(0x1.765118p-94 , "7.382097e-29"),
|
|
(0x1.cb25fep-94 , "9.055106e-29"),
|
|
(0x1.778decp-93 , "1.4813009e-28"),
|
|
(0x1.23f5d8p-92 , "2.303161e-28"),
|
|
(0x1.23f5d8p-90 , "9.212644e-28"),
|
|
(0x1.a1f86p-90 , "1.3188814e-27"),
|
|
(0x1.3db25cp-87 , "8.019793e-27"),
|
|
(0x1.bbb4e2p-87 , "1.1200729e-26"),
|
|
(0x1.83be04p-84 , "7.8303923e-26"),
|
|
(0x1.5c87fap-84 , "7.038531e-26"),
|
|
(0x1.b35478p-84 , "8.7914184e-26"),
|
|
(0x1.54279p-83 , "1.3738732e-25"),
|
|
(0x1.210beap-82 , "2.3348993e-25"),
|
|
(0x1.98040cp-82 , "3.2959255e-25"),
|
|
(0x1.d3801cp-81 , "7.552877e-25"),
|
|
(0x1.296c54p-80 , "9.610261e-25"),
|
|
(0x1.7ee2ccp-80 , "1.2371711e-24"),
|
|
(0x1.d3801cp-80 , "1.5105754e-24"),
|
|
(0x1.e8c296p-78 , "6.3170763e-24"),
|
|
(0x1.09a3c4p-77 , "6.8666256e-24"),
|
|
(0x1.7e7638p-77 , "9.886406e-24"),
|
|
(0x1.d3ecbp-77 , "1.2095566e-23"),
|
|
(0x1.5eada8p-76 , "1.8129645e-23"),
|
|
(0x1.21517ap-74 , "5.9829616e-23"),
|
|
(0x1.ab6668p-74 , "8.8384254e-23"),
|
|
(0x1.91172cp-74 , "8.2943575e-23"),
|
|
(0x1.be075ap-74 , "9.223658e-23"),
|
|
(0x1.aeaacap-70 , "1.424958e-21"),
|
|
(0x1.35db3cp-69 , "2.0504575e-21"),
|
|
(0x1.74df2p-67 , "9.869829e-21"),
|
|
(0x1.f5fa7ap-66 , "2.6574517e-20"),
|
|
(0x1.aa969ep-64 , "9.0333597e-20"),
|
|
(0x1.c77c72p-64 , "9.6452936e-20"),
|
|
(0x1.7f3dep-64 , "8.1154587e-20"),
|
|
(0x1.036eeap-57 , "7.0319526e-18"),
|
|
(0x1.46b2fap-57 , "8.855198e-18"),
|
|
(0x1.796e1ep-57 , "1.0230265e-17"),
|
|
(0x1.ce5b7ap-57 , "1.25322205e-17"),
|
|
(0x1.dee466p-57 , "1.2980399e-17"),
|
|
(0x1.5b3fep-55 , "3.7648867e-17"),
|
|
(0x1.3d11a2p-54 , "6.875335e-17"),
|
|
(0x1.796e1ep-54 , "8.184212e-17"),
|
|
(0x1.bbe57cp-54 , "9.625469e-17"),
|
|
(0x1.d624acp-54 , "1.01946067e-16"),
|
|
(0x1.f841f8p-54 , "1.0934346e-16"),
|
|
(0x1.3d11a2p-53 , "1.375067e-16"),
|
|
(0x1.b20fd8p-52 , "3.7648867e-16"),
|
|
(0x1.928636p-47 , "1.1172293e-14"),
|
|
(0x1.ba8a18p-47 , "1.2282937e-14"),
|
|
(0x1.e28dfap-47 , "1.3393581e-14"),
|
|
(0x1.374dcap-44 , "6.912334e-14"),
|
|
(0x1.a0efd4p-44 , "9.257857e-14"),
|
|
(0x1.7c8658p-44 , "8.4493474e-14"),
|
|
(0x1.e62862p-44 , "1.07948705e-13"),
|
|
(0x1.89537ap-40 , "1.397375e-12"),
|
|
(0x1.981532p-39 , "2.8996026e-12"),
|
|
(0x1.3e8744p-38 , "4.5265606e-12"),
|
|
(0x1.431bf6p-37 , "9.183316e-12"),
|
|
(0x1.525f7ep-37 , "9.6171395e-12"),
|
|
(0x1.7a0ff2p-37 , "1.07451764e-11"),
|
|
(0x1.cf8bp-37 , "1.3174684e-11"),
|
|
(0x1.f637d2p-37 , "1.4273895e-11"),
|
|
(0x1.4cc72ap-34 , "7.566495e-11"),
|
|
(0x1.86e8aep-34 , "8.8882395e-11"),
|
|
(0x1.9093e2p-34 , "9.1080816e-11"),
|
|
(0x1.eef76ap-34 , "1.12542343e-10"),
|
|
(0x1.f637d2p-34 , "1.1419116e-10"),
|
|
(0x1.2cc742p+59 , "6.772926e+17"),
|
|
(0x1.426638p+59 , "7.259787e+17"),
|
|
(0x1.ac1062p+60 , "1.9278289e+18"),
|
|
(0x1.3fb25ap+61 , "2.8795718e+18"),
|
|
(0x1.28aacp+68 , "3.4203376e+20"),
|
|
(0x1.21de92p+69 , "6.683934e+20"),
|
|
(0x1.077b62p+69 , "6.0754804e+20"),
|
|
(0x1.6dd68ep+69 , "8.435652e+20"),
|
|
(0x1.5b7194p+69 , "8.0115055e+20"),
|
|
(0x1.72d57p+71 , "3.4203376e+21"),
|
|
(0x1.3f1b66p+74 , "2.3545942e+22"),
|
|
(0x1.cf8accp+74 , "3.4203376e+22"),
|
|
(0x1.15204ep+76 , "8.179321e+22"),
|
|
(0x1.69167ep+76 , "1.0657433e+23"),
|
|
(0x1.a3dfccp+77 , "2.4784999e+23"),
|
|
(0x1.25ee82p+79 , "6.940265e+23"),
|
|
(0x1.6d4a0cp+79 , "8.6251485e+23"),
|
|
(0x1.9b811cp+79 , "9.716371e+23"),
|
|
(0x1.45ee1ep+84 , "2.4626585e+25"),
|
|
(0x1.84615ep+84 , "2.934519e+25"),
|
|
(0x1.1b492p+87 , "1.7123566e+26"),
|
|
(0x1.61594ap+87 , "2.1358624e+26"),
|
|
(0x1.1c0b3ep+89 , "6.8677604e+26"),
|
|
(0x1.3e5134p+89 , "7.696438e+26"),
|
|
(0x1.cd7a02p+89 , "1.1157819e+27"),
|
|
(0x1.621b68p+90 , "1.7123566e+27"),
|
|
(0x1.baa242p+93 , "1.7123566e+28"),
|
|
(0x1.d40dfp+94 , "3.6213959e+28"),
|
|
(0x1.10287cp+96 , "8.422887e+28"),
|
|
(0x1.8da532p+99 , "9.845221e+29"),
|
|
(0x1.f58b6cp+100, "2.4835287e+30"),
|
|
(0x1.e11158p+102, "9.5285285e+30"),
|
|
(0x1.b61814p+103, "1.7354693e+31"),
|
|
(0x1.0502cp+104 , "2.0679402e+31"),
|
|
(0x1.772604p+106, "1.188893e+32"),
|
|
(0x1.fe44a2p+106, "1.6171041e+32"),
|
|
(0x1.9c7f7ap+108, "5.229033e+32"),
|
|
(0x1.2c7314p+109, "7.6173e+32"),
|
|
(0x1.19c658p+109, "7.143839e+32"),
|
|
(0x1.89d2cp+109 , "9.984605e+32"),
|
|
(0x1.af2c36p+109, "1.0931527e+33"),
|
|
(0x1.c1d8f2p+109, "1.1404988e+33"),
|
|
(0x1.89d2cp+110 , "1.996921e+33"),
|
|
(0x1.97d44cp+110, "2.0679402e+33"),
|
|
(0x1.89d2cp+111 , "3.993842e+33"),
|
|
(0x1.2c7314p+112, "6.09384e+33"),
|
|
(0x1.368a68p+112, "6.2985127e+33"),
|
|
(0x1.89d2cp+112 , "7.987684e+33"),
|
|
(0x1.5b22eap+112, "7.040762e+33"),
|
|
(0x1.b88294p+112, "8.934606e+33"),
|
|
(0x1.e7326ap+112, "9.881528e+33"),
|
|
(0x1.672f5ap+114, "2.9140547e+34"),
|
|
(0x1.103662p+115, "4.4168992e+34"),
|
|
(0x1.4fd77p+116 , "1.0898681e+35"),
|
|
(0x1.cf198ap+116, "1.5028446e+35"),
|
|
(0x1.347374p+118, "4.0039227e+35"),
|
|
(0x1.7ced98p+118, "4.9447295e+35"),
|
|
(0x1.aa03cp+119 , "1.1059973e+36"),
|
|
(0x1.3b315cp+122, "6.5462986e+36"),
|
|
(0x1.3e2542p+122, "6.607624e+36"),
|
|
(0x1.c4fb6p+122 , "9.408067e+36"),
|
|
(0x1.f062b6p+122, "1.03095254e+37"),
|
|
(0x1.2db58cp+123, "1.2532508e+37"),
|
|
(0x1.a345d8p+126, "1.393273e+38"),
|
|
(0x1.a345d8p+127, "2.786546e+38")
|
|
]
|
|
|
|
// Of course, exhaustive testing of Double (or Float80!) is not
|
|
// practical, so I used another approach to generate test cases
|
|
// for those:
|
|
|
|
// The Errol paper details a method for enumerating cases where the optimal
|
|
// base-10 form might be extremely close to the midpoint between two binary
|
|
// Doubles, and therefore at risk of being handled incorrectly by Grisu-style
|
|
// formatters that use fixed-precision arithmetic. These are the extreme cases
|
|
// for our algorithm, so if we get these right, we have pretty high confidence
|
|
// that we get everything right.
|
|
|
|
// I took that list and ran it through a reduced-precision version of the Double
|
|
// formatter to identify these worst-case values:
|
|
fileprivate let generatedCases_Double: [(Double, String)] = [
|
|
(0x1.379f099a86228p-317, "4.559093100884257e-96"),
|
|
(0x1.7c3fba45c1271p-307, "5.696647848853893e-93"),
|
|
(0x1.4f14348a4c5dcp-299, "1.285104507361864e-90"),
|
|
(0x1.4f14348a4c5dcp-298, "2.570209014723728e-90"),
|
|
(0x1.a8c931c19b77ap-298, "3.258302752792233e-90"),
|
|
(0x1.4f14348a4c5dcp-297, "5.140418029447456e-90"),
|
|
(0x1.a8c931c19b77ap-297, "6.516605505584466e-90"),
|
|
(0x1.97a2a205f591fp-294, "5.002799281833755e-89"),
|
|
(0x1.387cf9cb4ad4fp-261, "3.294312317590731e-79"),
|
|
(0x1.ddc7e975c5045p-247, "8.252392874408775e-75"),
|
|
(0x1.ddc7e975c5045p-246, "1.650478574881755e-74"),
|
|
(0x1.ddc7e975c5045p-245, "3.30095714976351e-74"),
|
|
(0x1.ddc7e975c5045p-244, "6.60191429952702e-74"),
|
|
(0x1.ddc7e975c5045p-243, "1.320382859905404e-73"),
|
|
(0x1.ddc7e975c5045p-242, "2.640765719810808e-73"),
|
|
(0x1.ddc7e975c5045p-241, "5.281531439621616e-73"),
|
|
(0x1.9190e30e46c1ep-235, "2.840978519032327e-71"),
|
|
(0x1.0ed9bd6bfd003p-234, "3.832399419240467e-71"),
|
|
(0x1.9190e30e46c1ep-234, "5.681957038064654e-71"),
|
|
(0x1.3b28b27523ea6p-229, "1.426989259361117e-69"),
|
|
(0x1.3b28b27523ea6p-228, "2.853978518722234e-69"),
|
|
(0x1.aedaa0fc32ac8p-222, "2.497072464210591e-67"),
|
|
(0x1.aedaa0fc32ac8p-221, "4.994144928421182e-67"),
|
|
(0x1.48050091c3c25p-219, "1.520865118855779e-66"),
|
|
(0x1.48050091c3c25p-218, "3.041730237711558e-66"),
|
|
(0x1.f5a18504dfaadp-215, "3.721305106071689e-65"),
|
|
(0x1.f5a18504dfaadp-214, "7.442610212143378e-65"),
|
|
(0x1.eef5e1f90ac34p-196, "1.925091640472375e-59"),
|
|
(0x1.eef5e1f90ac34p-195, "3.85018328094475e-59"),
|
|
(0x1.eef5e1f90ac34p-194, "7.7003665618895e-59"),
|
|
(0x1.b20c2f4f8d49fp-138, "4.865841847892019e-42"),
|
|
(0x1.25d342b1e33e6p-128, "3.372948296445563e-39"),
|
|
(0x1.4faba79ea92edp-122, "2.466117547186101e-37"),
|
|
(0x1.4faba79ea92edp-121, "4.932235094372202e-37"),
|
|
(0x1.78cfcab31064dp-89 , "2.378016066134295e-27"),
|
|
(0x1.78cfcab31064dp-88 , "4.75603213226859e-27"),
|
|
(0x1.78cfcab31064dp-87 , "9.51206426453718e-27"),
|
|
(0x1.78cfcab31064dp-86 , "1.902412852907436e-26"),
|
|
(0x1.78cfcab31064dp-85 , "3.804825705814872e-26"),
|
|
(0x1.56d589dc3d0e3p-78 , "4.431027338341785e-24"),
|
|
(0x1.cd230a7ff47c4p+145, "8.034137530808823e+43"),
|
|
(0x1.30d9a1c3890bp+151 , "3.399192475886301e+45"),
|
|
(0x1.fc1562f08f125p+151, "5.665320793143835e+45"),
|
|
(0x1.a32ac316fb3acp+186, "1.605929046641989e+56"),
|
|
(0x1.a32ac316fb3acp+187, "3.211858093283978e+56"),
|
|
(0x1.8862481ccada3p+188, "6.013265967485603e+56"),
|
|
(0x1.a32ac316fb3acp+188, "6.423716186567956e+56"),
|
|
(0x1.5564fb098c956p+201, "4.285935458457607e+60"),
|
|
(0x1.f20b1a0d7f626p+207, "4.001624164855121e+62"),
|
|
(0x1.f20b1a0d7f626p+208, "8.003248329710242e+62"),
|
|
(0x1.a53bb31b369a2p+219, "1.386282306169174e+66"),
|
|
(0x1.a53bb31b369a2p+220, "2.772564612338348e+66"),
|
|
(0x1.a53bb31b369a2p+221, "5.545129224676696e+66"),
|
|
(0x1.66a00a69c6c34p+224, "3.776763733298609e+67"),
|
|
(0x1.e2785c3a2a20ap+227, "4.064803033949531e+68"),
|
|
(0x1.e2785c3a2a20ap+228, "8.129606067899062e+68"),
|
|
(0x1.454b1aef62c8dp+231, "4.384946084578497e+69"),
|
|
(0x1.0fde34c996086p+233, "1.465909318208761e+70"),
|
|
(0x1.0fde34c996086p+234, "2.931818636417522e+70"),
|
|
(0x1.9a2c2a34ac2fap+234, "4.423291694721855e+70"),
|
|
(0x1.9a2c2a34ac2fap+235, "8.84658338944371e+70"),
|
|
(0x1.9a2c2a34ac2fap+236, "1.769316677888742e+71"),
|
|
(0x1.9a2c2a34ac2fap+237, "3.538633355777484e+71"),
|
|
(0x1.9a2c2a34ac2fap+238, "7.077266711554968e+71"),
|
|
(0x1.ca9bade45b94ap+260, "3.318949537676913e+78"),
|
|
(0x1.ca9bade45b94ap+261, "6.637899075353826e+78"),
|
|
(0x1.b3a29c72cab91p+269, "1.614179517443508e+81"),
|
|
(0x1.b3a29c72cab91p+270, "3.228359034887016e+81"),
|
|
(0x1.b3a29c72cab91p+271, "6.456718069774032e+81"),
|
|
(0x1.c84c524ab5ebp+277 , "4.328301679886463e+83"),
|
|
(0x1.71760b3c0bc14p+287, "3.588703015985849e+86"),
|
|
(0x1.11926d079e00ap+304, "3.482974734743573e+91"),
|
|
(0x1.9d8f9fc2808d3p+321, "6.901257826767179e+96"),
|
|
(0x1.63ed4a60c9c91p+324, "4.751595491707413e+97")
|
|
]
|
|
|
|
// Float80 found via Errol technique.
|
|
//
|
|
// As with Double, except for Float80. The original list in this
|
|
// case had 23 million test cases. I filtered that against
|
|
// a Float80 formatter with 129 bits precision.
|
|
fileprivate let generatedCases_Float80: [(Float80, String)] = [
|
|
(0xf.8f06b25f79a0ad9p-329, "1.4226714622425547106e-98"),
|
|
(0xb.a14796a877c6939p-303, "7.136593768505787697e-91"),
|
|
(0xf.8e473d72ad52a71p-296, "1.2218360180048428346e-88"),
|
|
(0xb.c6f5bb0c6811badp-289, "1.1840575897174935119e-86"),
|
|
(0xc.c2111d383f1adfp-236 , "1.1553302055733876345e-70"),
|
|
(0xc.c2111d383f1adfp-233 , "9.242641644587101076e-70"),
|
|
(0xf.be3d87da323be87p-156, "1.7235014706998294962e-46"),
|
|
(0xb.d809f3772d38a59p-150, "8.298420730548195494e-45"),
|
|
(0xb.61d88e478a80191p-136, "1.3066137006059396529e-40"),
|
|
(0x8.7e6e301b42330f6p-115, "2.044824541427335683e-34"),
|
|
(0xe.280cfad818ffc45p-115, "3.408040902378892805e-34"),
|
|
(0x8.7e6e301b42330f6p-114, "4.089649082854671366e-34"),
|
|
(0xe.280cfad818ffc45p-114, "6.81608180475778561e-34"),
|
|
(0x8.7e6e301b42330f6p-113, "8.179298165709342732e-34"),
|
|
(0xe.280cfad818ffc45p-113, "1.363216360951557122e-33"),
|
|
(0xe.280cfad818ffc45p-112, "2.726432721903114244e-33"),
|
|
(0xe.280cfad818ffc45p-111, "5.452865443806228488e-33"),
|
|
(0xe.280cfad818ffc45p-110, "1.0905730887612456976e-32"),
|
|
(0xc.09de12b2b8b462p+169 , "9.008308715099773956e+51"),
|
|
(0xc.eb461a5ceb157bep+196, "1.2975058974374774429e+60"),
|
|
(0xd.f29472fdfc52a5ep+202, "8.965157263531703087e+61"),
|
|
(0x9.63a86496b5f39b5p+206, "9.656322849684964617e+62"),
|
|
(0xf.aab4e43915de71bp+239, "1.3840439837858165139e+73"),
|
|
(0xf.135bf3c301b911ap+239, "1.3318159089259743813e+73"),
|
|
(0xe.7c03034ced93b19p+239, "1.2795878340661322487e+73"),
|
|
(0xd.e4aa12d6d96e518p+239, "1.2273597592062901161e+73"),
|
|
(0xd.4d512260c548f17p+239, "1.1751316843464479835e+73"),
|
|
(0xc.b5f831eab123916p+239, "1.1229036094866058509e+73"),
|
|
(0xc.1e9f41749cfe315p+239, "1.0706755346267637183e+73"),
|
|
(0xa.efed608874b3713p+239, "9.662193849070794531e+72"),
|
|
(0xa.58947012608e112p+239, "9.139913100472373205e+72"),
|
|
(0x9.c13b7f9c4c68b11p+239, "8.617632351873951879e+72"),
|
|
(0x9.29e28f26384351p+239 , "8.095351603275530553e+72"),
|
|
(0x8.92899eb0241df0fp+239, "7.573070854677109227e+72"),
|
|
(0xa.58947012608e112p+240, "1.827982620094474641e+73"),
|
|
(0xa.58947012608e112p+241, "3.655965240188949282e+73"),
|
|
(0xa.58947012608e112p+242, "7.311930480377898564e+73"),
|
|
(0xd.4d512260c548f17p+242, "9.401053474771583868e+73"),
|
|
(0xc.9240ee0f9d5e4d6p+252, "9.097859174935622588e+76"),
|
|
]
|
|
|
|
let PrintTests = TestSuite("FloatingPointPrinting")
|
|
|
|
% for FloatType in ['Float', 'Double', 'Float80']:
|
|
% if FloatType == 'Float80':
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
% end
|
|
|
|
// Verify that a particular value provides a specific description string.
|
|
// Also check that the generated strings actually satisfy our
|
|
// accuracy requirements.
|
|
fileprivate func expectDescription(_ expected: String, _ object: ${FloatType}, ${TRACE}) {
|
|
expectEqual(expected, object.description, ${trace})
|
|
expectEqual(expected, object.debugDescription, ${trace})
|
|
expectAccurateDescription(object, ${trace})
|
|
}
|
|
|
|
// Verify our key requirements:
|
|
//
|
|
// * Accurate. A formatted float should parse back to exactly the original
|
|
// value.
|
|
//
|
|
// * Short. The formatted value should use the minimum number of digits needed
|
|
// to be accurate.
|
|
//
|
|
// * Close. If there is more than one accurate and short value, we want the one
|
|
// that is closest (as an infinitely-precise real number) to the original
|
|
// binary float (interpreted as an infinitely-precise real number).
|
|
fileprivate func expectAccurateDescription(_ object: ${FloatType}, ${TRACE}) {
|
|
if !object.isFinite {
|
|
return
|
|
}
|
|
|
|
// Following checks all return early on failure, since it makes no sense to
|
|
// check shortness if the result is inaccurate, etc.
|
|
|
|
// Verify round-trip accuracy:
|
|
let text = object.debugDescription
|
|
if let roundTrip = ${FloatType}(text) {
|
|
if object != roundTrip {
|
|
expectationFailure("Round-trip inaccuracy: \(object) != \(roundTrip)", trace: ${trace})
|
|
return
|
|
}
|
|
} else {
|
|
expectationFailure("Failed to parse \(text)", trace: ${trace})
|
|
return
|
|
}
|
|
|
|
// TODO: Verify shortness by trimming the last digit and checking
|
|
// that the result does NOT round-trip.
|
|
|
|
// TODO: Verify closeness by fuzzing the last digit and checking
|
|
// that the result is not closer. Note this requires higher-precision
|
|
// arithmetic.
|
|
}
|
|
% if FloatType == 'Float80':
|
|
#endif
|
|
% end
|
|
% end
|
|
|
|
// Special helper for NaN values
|
|
fileprivate func expectNaN<T>(_ expected: String, _ object: T, ${TRACE}) where T: FloatingPoint & CustomDebugStringConvertible & CustomStringConvertible {
|
|
// Regular description always returns "nan"
|
|
expectEqual("nan", object.description, ${trace})
|
|
// Debug description returns a more detailed form:
|
|
expectEqual(expected, object.debugDescription, ${trace})
|
|
}
|
|
|
|
// Foundation's String(format:) isn't available here, so
|
|
// build up "1e-234" manually:
|
|
fileprivate func exponentialPowerOfTen(_ power: Int) -> String {
|
|
let digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
|
var s = "1e"
|
|
var p = power
|
|
if p < 0 {
|
|
s += "-"
|
|
p = -p
|
|
} else {
|
|
s += "+"
|
|
}
|
|
if (p > 999) {
|
|
s += digits[ (p / 1000) % 10]
|
|
}
|
|
if (p > 99) {
|
|
s += digits[ (p / 100) % 10]
|
|
}
|
|
s += digits[ (p / 10) % 10]
|
|
s += digits[ p % 10]
|
|
return s
|
|
}
|
|
|
|
// An earlier version of Swift's floating-point `.description` logic
|
|
// used potentially locale-sensitive C library functions, hence
|
|
// this logic to help verify that the output does not depend on
|
|
// the C locale.
|
|
PrintTests.setUp {
|
|
if let localeArgIndex = CommandLine.arguments.index(of: "--locale") {
|
|
let locale = CommandLine.arguments[localeArgIndex + 1]
|
|
expectEqual("ru_RU.UTF-8", locale)
|
|
setlocale(LC_ALL, locale)
|
|
} else {
|
|
setlocale(LC_ALL, "")
|
|
}
|
|
}
|
|
|
|
// Check that all floating point types
|
|
// are CustomStringConvertible
|
|
PrintTests.test("CustomStringConvertible") {
|
|
func hasDescription(_ any: Any) {
|
|
expectTrue(any is CustomStringConvertible)
|
|
}
|
|
|
|
hasDescription(Float(1.0))
|
|
hasDescription(Double(1.0))
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
hasDescription(Float80(1.0))
|
|
#endif
|
|
hasDescription(CFloat(1.0))
|
|
hasDescription(CDouble(1.0))
|
|
}
|
|
|
|
// Check that all floating point types
|
|
// are CustomDebugStringConvertible
|
|
PrintTests.test("CustomDebugStringConvertible") {
|
|
func hasDebugDescription(_ any: Any) {
|
|
expectTrue(any is CustomDebugStringConvertible)
|
|
}
|
|
|
|
hasDebugDescription(Float(1.0))
|
|
hasDebugDescription(Double(1.0))
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
hasDebugDescription(Float80(1.0))
|
|
#endif
|
|
hasDebugDescription(CFloat(1.0))
|
|
hasDebugDescription(CDouble(1.0))
|
|
}
|
|
|
|
PrintTests.test("Printable_CFloat") {
|
|
// Basic check for CFloat: Since it's a synonym for Float, we don't
|
|
// need more detailed verification, just basic sanity.
|
|
expectDescription("1.0", CFloat(1.0))
|
|
expectDescription("1.1", CFloat(1.1))
|
|
expectDescription("-1.0", CFloat(-1.0))
|
|
}
|
|
|
|
PrintTests.test("Printable_CDouble") {
|
|
// Likewise for CDouble
|
|
expectDescription("1.0", CDouble(1.0))
|
|
expectDescription("1.1", CDouble(1.1))
|
|
expectDescription("-1.0", CDouble(-1.0))
|
|
}
|
|
|
|
PrintTests.test("Printable_Float") {
|
|
func asFloat32(_ f: Float32) -> Float32 { return f }
|
|
|
|
// Basic sanity checks:
|
|
let f = 100.125 as Float
|
|
expectEqual("f = 100.125", "f = \(f)")
|
|
|
|
expectDescription("0.0", asFloat32(0.0))
|
|
expectDescription("-0.0", -asFloat32(0.0))
|
|
expectDescription("0.1", asFloat32(0.1))
|
|
expectDescription("-0.1", asFloat32(-0.1))
|
|
expectDescription("1.0", asFloat32(1.0))
|
|
expectDescription("-1.0", asFloat32(-1.0))
|
|
expectDescription("1.1", asFloat32(1.1))
|
|
expectDescription("100.125", asFloat32(100.125))
|
|
expectDescription("-100.125", asFloat32(-100.125))
|
|
|
|
// Standard special numbers:
|
|
expectDescription("inf", Float.infinity)
|
|
expectDescription("-inf", -Float.infinity)
|
|
expectDescription("3.1415925", Float.pi)
|
|
expectDescription("3.4028235e+38", Float.greatestFiniteMagnitude)
|
|
expectDescription("1e-45", Float.leastNonzeroMagnitude)
|
|
expectDescription("1.1754944e-38", Float.leastNormalMagnitude)
|
|
|
|
// Special cases for the underlying algorithms:
|
|
// Smallest Float that requires 9 digits to print accurately
|
|
expectDescription("1.00000075e-36", 1.00000075e-36 as Float)
|
|
// Worst case for shortness:
|
|
// Float for which the shortest accurate decimal form is
|
|
// closest to the midpoint between two binary floats
|
|
expectDescription("7.0385313e-26", 7.0385313e-26 as Float)
|
|
// Second-worst case for shortness:
|
|
expectDescription("7.038531e-26", 7.038531e-26 as Float)
|
|
|
|
// NaNs require special care in testing:
|
|
// NaN is printed with additional detail to debugDescription, but not description
|
|
expectNaN("nan", Float.nan)
|
|
expectNaN("nan(0xffff)", Float(nan: 65535, signaling: false))
|
|
expectNaN("nan(0x1fffff)", Float(bitPattern: 0x7fff_ffff))
|
|
expectNaN("nan(0x1fffff)", Float(bitPattern: 0x7fdf_ffff))
|
|
#if !arch(arm)
|
|
// ARM doesn't have negative Float nans
|
|
expectNaN("-nan", -Float.nan)
|
|
expectNaN("-nan(0xffff)", -Float(nan: 65535, signaling: false)) // fail
|
|
#endif
|
|
#if !arch(i386) && !arch(arm)
|
|
// 32-bit i386 and ARM lack signaling Float nans
|
|
expectNaN("snan", Float.signalingNaN)
|
|
expectNaN("-snan", -Float.signalingNaN)
|
|
expectNaN("snan(0xffff)", Float(nan: 65535, signaling: true))
|
|
expectNaN("-snan(0xffff)", -Float(nan: 65535, signaling: true))
|
|
expectNaN("snan(0x1fffff)", Float(bitPattern: 0x7fbf_ffff))
|
|
#endif
|
|
|
|
// Every power of 10 should print with only a single digit '1'
|
|
for power in -45 ... 38 {
|
|
let s: String
|
|
if power < -4 || power > 5 { // Exponential form
|
|
s = exponentialPowerOfTen(power)
|
|
} else if power < 0 { // Fractional decimal form
|
|
s = "0." + String(repeating: "0", count: -power - 1) + "1"
|
|
} else { // Decimal form
|
|
s = "1" + String(repeating: "0", count: power) + ".0"
|
|
}
|
|
let f = Float(s)!
|
|
expectDescription(s, f)
|
|
}
|
|
|
|
// Test the 170 "worst cases" listed above.
|
|
for (f,s) in generatedCases_Float {
|
|
expectAccurateDescription(f.nextDown)
|
|
expectDescription(s, f)
|
|
expectAccurateDescription(f.nextUp)
|
|
}
|
|
|
|
// Check that the formatter chooses exponential
|
|
// format when it should:
|
|
expectDescription("1.00001", asFloat32(1.00001))
|
|
expectDescription("1.25e+17", asFloat32(125000000000000000.0))
|
|
expectDescription("1.25e+16", asFloat32(12500000000000000.0))
|
|
expectDescription("1.25e+15", asFloat32(1250000000000000.0))
|
|
expectDescription("1.25e+14", asFloat32(125000000000000.0))
|
|
expectDescription("1.25e+13", asFloat32(12500000000000.0))
|
|
expectDescription("1.25e+12", asFloat32(1250000000000.0))
|
|
expectDescription("1.25e+11", asFloat32(125000000000.0))
|
|
expectDescription("1.25e+10", asFloat32(12500000000.0))
|
|
expectDescription("1.25e+09", asFloat32(1250000000.0))
|
|
expectDescription("1.25e+08", asFloat32(125000000.0))
|
|
expectDescription("1.25e+07", asFloat32(12500000.0))
|
|
expectDescription("1.25e+06", asFloat32(1250000.0))
|
|
expectDescription("125000.0", asFloat32(125000.0))
|
|
expectDescription("12500.0", asFloat32(12500.0))
|
|
expectDescription("1250.0", asFloat32(1250.0))
|
|
expectDescription("125.0", asFloat32(125.0))
|
|
expectDescription("12.5", asFloat32(12.5))
|
|
expectDescription("1.25", asFloat32(1.25))
|
|
expectDescription("0.125", asFloat32(0.125))
|
|
expectDescription("0.0125", asFloat32(0.0125))
|
|
expectDescription("0.00125", asFloat32(0.00125))
|
|
expectDescription("0.000125", asFloat32(0.000125))
|
|
expectDescription("1.25e-05", asFloat32(0.0000125))
|
|
expectDescription("1.25e-06", asFloat32(0.00000125))
|
|
expectDescription("1.25e-07", asFloat32(0.000000125))
|
|
expectDescription("1.25e-08", asFloat32(0.0000000125))
|
|
expectDescription("1.25e-09", asFloat32(0.00000000125))
|
|
expectDescription("1.25e-10", asFloat32(0.000000000125))
|
|
expectDescription("1.25e-11", asFloat32(0.0000000000125))
|
|
expectDescription("1.25e-12", asFloat32(0.00000000000125))
|
|
expectDescription("1.25e-13", asFloat32(0.000000000000125))
|
|
expectDescription("1.25e-14", asFloat32(0.0000000000000125))
|
|
expectDescription("1.25e-15", asFloat32(0.00000000000000125))
|
|
expectDescription("1.25e-16", asFloat32(0.000000000000000125))
|
|
expectDescription("1.25e-17", asFloat32(0.0000000000000000125))
|
|
}
|
|
|
|
PrintTests.test("Printable_Double") {
|
|
func asFloat64(_ f: Float64) -> Float64 { return f }
|
|
|
|
// Sanity check
|
|
let f = 100.125 as Double
|
|
expectEqual("f = 100.125", "f = \(f)")
|
|
|
|
expectDescription("0.0", asFloat64(0.0))
|
|
expectDescription("-0.0", asFloat64(-0.0))
|
|
expectDescription("0.1", asFloat64(0.1))
|
|
expectDescription("-0.1", asFloat64(-0.1))
|
|
expectDescription("1.0", asFloat64(1.0))
|
|
expectDescription("-1.0", asFloat64(-1.0))
|
|
expectDescription("1.1", asFloat64(1.1))
|
|
expectDescription("100.125", asFloat64(100.125))
|
|
expectDescription("-100.125", asFloat64(-100.125))
|
|
|
|
// Special values
|
|
expectDescription("3.141592653589793", Double.pi)
|
|
expectDescription("1.7976931348623157e+308", Double.greatestFiniteMagnitude)
|
|
expectDescription("5e-324", Double.leastNonzeroMagnitude)
|
|
expectDescription("2.2250738585072014e-308", Double.leastNormalMagnitude)
|
|
expectDescription("inf", Double.infinity)
|
|
expectDescription("-inf", -Double.infinity)
|
|
// Worst case for Double shortness:
|
|
expectDescription("2.311989689387339e-82", 2.311989689387339e-82)
|
|
|
|
// Verify NaNs
|
|
expectNaN("nan", Double.nan)
|
|
expectNaN("-nan", -Double.nan)
|
|
#if arch(i386)
|
|
expectNaN("nan", Double(nan: 65535, signaling: false))
|
|
expectNaN("nan", Float64(bitPattern: 0x7fff_ffff_ffff_ffff))
|
|
expectNaN("nan", Float64(bitPattern: 0x7ffb_ffff_ffff_ffff))
|
|
expectNaN("-nan", -Double(nan: 65535, signaling: false))
|
|
expectNaN("nan", Double.signalingNaN)
|
|
expectNaN("-nan", -Double.signalingNaN)
|
|
expectNaN("nan", Double(nan: 65535, signaling: true))
|
|
expectNaN("-nan", -Double(nan: 65535, signaling: true))
|
|
expectNaN("nan", Float64(bitPattern: 0x7ff7_ffff_ffff_ffff))
|
|
#else
|
|
// 32-bit i386 lacks signaling Double nans or payloads on NaNs
|
|
expectNaN("nan(0xffff)", Double(nan: 65535, signaling: false))
|
|
expectNaN("nan(0x3ffffffffffff)", Float64(bitPattern: 0x7fff_ffff_ffff_ffff))
|
|
expectNaN("nan(0x3ffffffffffff)", Float64(bitPattern: 0x7ffb_ffff_ffff_ffff))
|
|
expectNaN("-nan(0xffff)", -Double(nan: 65535, signaling: false))
|
|
expectNaN("snan", Double.signalingNaN)
|
|
expectNaN("-snan", -Double.signalingNaN)
|
|
expectNaN("snan(0xffff)", Double(nan: 65535, signaling: true))
|
|
expectNaN("-snan(0xffff)", -Double(nan: 65535, signaling: true))
|
|
expectNaN("snan(0x3ffffffffffff)", Float64(bitPattern: 0x7ff7_ffff_ffff_ffff))
|
|
#endif
|
|
|
|
// We know how every power of 10 should print
|
|
for power in -323 ... 308 {
|
|
let s: String
|
|
if power < -4 || power > 14 { // Exponential form
|
|
s = exponentialPowerOfTen(power)
|
|
} else if power < 0 { // Fractional decimal form
|
|
s = "0." + String(repeating: "0", count: -power - 1) + "1"
|
|
} else { // Decimal form
|
|
s = "1" + String(repeating: "0", count: power) + ".0"
|
|
}
|
|
let f = Double(s)!
|
|
expectDescription(s, f)
|
|
}
|
|
|
|
// Verify 74 extreme values generated using a technique from the Errol paper,
|
|
// plus many nearby values.
|
|
for (d, s) in generatedCases_Double {
|
|
expectDescription(s, d)
|
|
|
|
// Also check nearby values.
|
|
var upCase = d
|
|
var downCase = d
|
|
for _ in 0..<10 {
|
|
upCase = upCase.nextUp
|
|
expectAccurateDescription(upCase)
|
|
downCase = downCase.nextDown
|
|
expectAccurateDescription(downCase)
|
|
}
|
|
}
|
|
|
|
// Check that the formatter chooses exponential
|
|
// format when it should:
|
|
expectDescription("1.00000000000001", asFloat64(1.00000000000001))
|
|
expectDescription("1.25e+17", asFloat64(125000000000000000.0))
|
|
expectDescription("1.25e+16", asFloat64(12500000000000000.0))
|
|
expectDescription("1.25e+15", asFloat64(1250000000000000.0))
|
|
expectDescription("125000000000000.0", asFloat64(125000000000000.0))
|
|
expectDescription("12500000000000.0", asFloat64(12500000000000.0))
|
|
expectDescription("1250000000000.0", asFloat64(1250000000000.0))
|
|
expectDescription("125000000000.0", asFloat64(125000000000.0))
|
|
expectDescription("12500000000.0", asFloat64(12500000000.0))
|
|
expectDescription("1250000000.0", asFloat64(1250000000.0))
|
|
expectDescription("125000000.0", asFloat64(125000000.0))
|
|
expectDescription("12500000.0", asFloat64(12500000.0))
|
|
expectDescription("1250000.0", asFloat64(1250000.0))
|
|
expectDescription("125000.0", asFloat64(125000.0))
|
|
expectDescription("12500.0", asFloat64(12500.0))
|
|
expectDescription("1250.0", asFloat64(1250.0))
|
|
expectDescription("125.0", asFloat64(125.0))
|
|
expectDescription("12.5", asFloat64(12.5))
|
|
expectDescription("1.25", asFloat64(1.25))
|
|
expectDescription("0.125", asFloat64(0.125))
|
|
expectDescription("0.0125", asFloat64(0.0125))
|
|
expectDescription("0.00125", asFloat64(0.00125))
|
|
expectDescription("0.000125", asFloat64(0.000125))
|
|
expectDescription("1.25e-05", asFloat64(0.0000125))
|
|
expectDescription("1.25e-06", asFloat64(0.00000125))
|
|
expectDescription("1.25e-07", asFloat64(0.000000125))
|
|
expectDescription("1.25e-08", asFloat64(0.0000000125))
|
|
expectDescription("1.25e-09", asFloat64(0.00000000125))
|
|
expectDescription("1.25e-10", asFloat64(0.000000000125))
|
|
expectDescription("1.25e-11", asFloat64(0.0000000000125))
|
|
expectDescription("1.25e-12", asFloat64(0.00000000000125))
|
|
expectDescription("1.25e-13", asFloat64(0.000000000000125))
|
|
expectDescription("1.25e-14", asFloat64(0.0000000000000125))
|
|
expectDescription("1.25e-15", asFloat64(0.00000000000000125))
|
|
expectDescription("1.25e-16", asFloat64(0.000000000000000125))
|
|
expectDescription("1.25e-17", asFloat64(0.0000000000000000125))
|
|
}
|
|
|
|
PrintTests.test("Printable_Float80") {
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
func asFloat80(_ f: Swift.Float80) -> Swift.Float80 { return f }
|
|
|
|
// Sanity
|
|
let f = 100.125 as Float80
|
|
expectEqual("f = 100.125", "f = \(f)")
|
|
|
|
expectDescription("0.0", asFloat80(0.0))
|
|
expectDescription("-0.0", -asFloat80(0.0))
|
|
expectDescription("0.1", asFloat80(0.1))
|
|
expectDescription("-0.1", asFloat80(-0.1))
|
|
expectDescription("1.0", asFloat80(1.0))
|
|
expectDescription("-1.0", asFloat80(-1.0))
|
|
expectDescription("1.1", asFloat80(1.1))
|
|
expectDescription("100.125", asFloat80(100.125))
|
|
expectDescription("-100.125", asFloat80(-100.125))
|
|
|
|
// Special values
|
|
expectDescription("3.1415926535897932385", Float80.pi)
|
|
expectDescription("1.189731495357231765e+4932", Float80.greatestFiniteMagnitude)
|
|
expectDescription("4e-4951", Float80.leastNonzeroMagnitude)
|
|
expectDescription("3.3621031431120935063e-4932", Float80.leastNormalMagnitude)
|
|
expectDescription("inf", Float80.infinity)
|
|
expectDescription("-inf", -Float80.infinity)
|
|
|
|
// NaNs
|
|
expectNaN("nan", Float80.nan)
|
|
expectNaN("nan(0xffff)", Float80(nan: 65535, signaling: false))
|
|
expectNaN("nan(0x1fffffffffffffff)", Float80(sign: .plus, exponentBitPattern: 0x7fff, significandBitPattern: 0xffff_ffff_ffff_ffff))
|
|
expectNaN("nan(0x1fffffffffffffff)", Float80(sign: .plus, exponentBitPattern: 0x7fff, significandBitPattern: 0xdfff_ffff_ffff_ffff))
|
|
expectNaN("-nan", -Float80.nan)
|
|
expectNaN("-nan(0xffff)", -Float80(nan: 65535, signaling: false))
|
|
expectNaN("snan", Float80.signalingNaN)
|
|
expectNaN("-snan", -Float80.signalingNaN)
|
|
expectNaN("snan(0xffff)", Float80(nan: 65535, signaling: true))
|
|
expectNaN("-snan(0xffff)", -Float80(nan: 65535, signaling: true))
|
|
expectNaN("snan(0x1fffffffffffffff)", Float80(sign: .plus, exponentBitPattern: 0x7fff, significandBitPattern: 0xbfff_ffff_ffff_ffff))
|
|
|
|
// We know how every power of 10 should print
|
|
for power in -4950 ... 4932 {
|
|
let s: String
|
|
if power < -4 || power > 17 { // Exponential form
|
|
s = exponentialPowerOfTen(power)
|
|
} else if power < 0 { // Fractional decimal form
|
|
s = "0." + String(repeating: "0", count: -power - 1) + "1"
|
|
} else { // Decimal form
|
|
s = "1" + String(repeating: "0", count: power) + ".0"
|
|
}
|
|
let f = Float80(s)!
|
|
expectDescription(s, f)
|
|
}
|
|
|
|
// Verify the extreme cases generated via the Errol technique.
|
|
for (d, s) in generatedCases_Float80 {
|
|
expectDescription(s, d)
|
|
|
|
var upCase = d
|
|
var downCase = d
|
|
for _ in 0..<10 {
|
|
upCase = upCase.nextUp
|
|
expectAccurateDescription(upCase)
|
|
downCase = downCase.nextDown
|
|
expectAccurateDescription(downCase)
|
|
}
|
|
}
|
|
|
|
// Check that the formatter chooses exponential
|
|
// format when it should:
|
|
expectDescription("1.00000000000000001", asFloat80(1.00000000000000001))
|
|
expectDescription("1.25e+19", asFloat80(12500000000000000000.0))
|
|
expectDescription("1.25e+18", asFloat80(1250000000000000000.0))
|
|
expectDescription("125000000000000000.0", asFloat80(125000000000000000.0))
|
|
expectDescription("12500000000000000.0", asFloat80(12500000000000000.0))
|
|
expectDescription("1250000000000000.0", asFloat80(1250000000000000.0))
|
|
expectDescription("125000000000000.0", asFloat80(125000000000000.0))
|
|
expectDescription("12500000000000.0", asFloat80(12500000000000.0))
|
|
expectDescription("1250000000000.0", asFloat80(1250000000000.0))
|
|
expectDescription("125000000000.0", asFloat80(125000000000.0))
|
|
expectDescription("12500000000.0", asFloat80(12500000000.0))
|
|
expectDescription("1250000000.0", asFloat80(1250000000.0))
|
|
expectDescription("125000000.0", asFloat80(125000000.0))
|
|
expectDescription("12500000.0", asFloat80(12500000.0))
|
|
expectDescription("1250000.0", asFloat80(1250000.0))
|
|
expectDescription("125000.0", asFloat80(125000.0))
|
|
expectDescription("12500.0", asFloat80(12500.0))
|
|
expectDescription("1250.0", asFloat80(1250.0))
|
|
expectDescription("125.0", asFloat80(125.0))
|
|
expectDescription("12.5", asFloat80(12.5))
|
|
expectDescription("1.25", asFloat80(1.25))
|
|
expectDescription("0.125", asFloat80(0.125))
|
|
expectDescription("0.0125", asFloat80(0.0125))
|
|
expectDescription("0.00125", asFloat80(0.00125))
|
|
expectDescription("0.000125", asFloat80(0.000125))
|
|
expectDescription("1.25e-05", asFloat80(0.0000125))
|
|
expectDescription("1.25e-06", asFloat80(0.00000125))
|
|
expectDescription("1.25e-07", asFloat80(0.000000125))
|
|
expectDescription("1.25e-08", asFloat80(0.0000000125))
|
|
expectDescription("1.25e-09", asFloat80(0.00000000125))
|
|
expectDescription("1.25e-10", asFloat80(0.000000000125))
|
|
expectDescription("1.25e-11", asFloat80(0.0000000000125))
|
|
expectDescription("1.25e-12", asFloat80(0.00000000000125))
|
|
expectDescription("1.25e-13", asFloat80(0.000000000000125))
|
|
expectDescription("1.25e-14", asFloat80(0.0000000000000125))
|
|
expectDescription("1.25e-15", asFloat80(0.00000000000000125))
|
|
expectDescription("1.25e-16", asFloat80(0.000000000000000125))
|
|
expectDescription("1.25e-17", asFloat80(0.0000000000000000125))
|
|
#endif
|
|
}
|
|
|
|
runAllTests()
|