mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This replaces swiftMSVCRT with swiftCRT. The big difference here is that the `visualc` module is no longer imported nor exported. The `visualc` module remains in use for a singular test wrt availability, but this should effectively remove the need for the `visualc` module. The difference between the MSVCRT and ucrt module was not well understood by most. MSVCRT provided ucrt AND visualc, combining pieces of the old MSVCRT and the newer ucrt. The ucrt module is what you really wanted most of the time, however, would need to use MSVCRT for the convenience aliases for type-generic math and the deprecated math constants. Unfortunately, we cannot shadow the `ucrt` module and create a Swift SDK overlay for ucrt as that seems to result in circular dependencies when processing the `_Concurrency` module. Although this makes using the C library easier for most people, it has a more important subtle change: it cleaves the dependency on visualc. This means that this enables use of Swift without Visual Studio for the singular purpose of providing 3 header files. Additionally, it removes the need for the installation of 2 of the 4 support files. This greatly simplifies the deployment process on Windows.
1104 lines
45 KiB
Swift
1104 lines
45 KiB
Swift
// 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: %target-codesign %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
|
|
|
|
// With a non-optimized stdlib the test takes very long.
|
|
// REQUIRES: optimized_stdlib
|
|
|
|
import StdlibUnittest
|
|
#if canImport(Darwin)
|
|
import Darwin
|
|
#elseif canImport(Glibc)
|
|
import Glibc
|
|
#elseif os(Windows)
|
|
import CRT
|
|
#else
|
|
#error("Unsupported platform")
|
|
#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")
|
|
]
|
|
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
// 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"),
|
|
]
|
|
#endif
|
|
|
|
let PrintTests = TestSuite("FloatingPointPrinting")
|
|
|
|
% for FloatType in ['Float16', 'Float', 'Double', 'Float80']:
|
|
% if FloatType == 'Float16':
|
|
@available(iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
@available(macOS, unavailable)
|
|
@available(macCatalyst, unavailable)
|
|
% end
|
|
% 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},
|
|
_ message: @autoclosure () -> String = "",
|
|
stackTrace: SourceLocStack = SourceLocStack(),
|
|
showFrame: Bool = true,
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
expectEqual(expected, object.description,
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
expectEqual(expected, object.debugDescription,
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
expectAccurateDescription(object,
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
}
|
|
|
|
// 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).
|
|
% if FloatType == 'Float16':
|
|
@available(iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
@available(macOS, unavailable)
|
|
@available(macCatalyst, unavailable)
|
|
% end
|
|
fileprivate func expectAccurateDescription(_ object: ${FloatType},
|
|
_ message: @autoclosure () -> String = "",
|
|
stackTrace: SourceLocStack = SourceLocStack(),
|
|
showFrame: Bool = true,
|
|
file: String = #file, line: UInt = #line
|
|
) {
|
|
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: message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
return
|
|
}
|
|
} else {
|
|
expectationFailure("Failed to parse \(text)",
|
|
trace: message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
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,
|
|
_ message: @autoclosure () -> String = "",
|
|
stackTrace: SourceLocStack = SourceLocStack(),
|
|
showFrame: Bool = true,
|
|
file: String = #file, line: UInt = #line
|
|
) where T: FloatingPoint & CustomDebugStringConvertible & CustomStringConvertible {
|
|
// Regular description always returns "nan"
|
|
expectEqual("nan", object.description,
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
|
|
// debugDescription tries to print details about NaNs, which is tricky to test.
|
|
|
|
/*
|
|
// We cannot reliably test the exact expected string, because various
|
|
// implementations force all NaNs quiet, discard payloads, or clear sign bits.
|
|
// In some cases, just passing a NaN into a function (via an FP register) is
|
|
// enough to mangle the value.
|
|
expectEqual(expected, object.debugDescription,
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
*/
|
|
|
|
// We can verify that the generated debugDescription text
|
|
// follows the expected general format, even when we can't verify the exact value.
|
|
var actual = object.debugDescription
|
|
|
|
// Optional leading "-"
|
|
if actual.hasPrefix("-") {
|
|
actual = String(actual.dropFirst())
|
|
}
|
|
|
|
// Optional leading "s"
|
|
if actual.hasPrefix("s") {
|
|
actual = String(actual.dropFirst())
|
|
}
|
|
|
|
// Fixed text "nan"
|
|
if actual.prefix(3) != "nan" {
|
|
expectationFailure("Badly formatted NaN debug description (expected 'nan'): \(object.debugDescription)", trace:
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
return
|
|
}
|
|
actual = String(actual.dropFirst(3))
|
|
|
|
// Optional parenthesized payload after "nan"
|
|
if actual.hasPrefix("(0x") {
|
|
actual = String(actual.dropFirst(3))
|
|
while !actual.isEmpty {
|
|
if actual.hasPrefix(")") {
|
|
if actual != ")" {
|
|
expectationFailure("Malformed NaN: extra text after payload: \(object.debugDescription)", trace:
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
}
|
|
return
|
|
} else {
|
|
// TODO: verify hex digit
|
|
}
|
|
actual = String(actual.dropFirst())
|
|
}
|
|
expectationFailure("Malformed NaN: no closing parenthesis after payload: \(object.debugDescription)", trace:
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
} else if !actual.isEmpty {
|
|
expectationFailure("Badly formatted NaN debug description has invalid text after 'nan'. Expected '(0x': \(object.debugDescription)", trace:
|
|
message(),
|
|
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line))
|
|
}
|
|
}
|
|
|
|
// 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.firstIndex(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))
|
|
}
|
|
|
|
#if !os(macOS) && !(os(iOS) && targetEnvironment(macCatalyst))
|
|
if #available(iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
|
|
PrintTests.test("Printable_Float16") {
|
|
func asFloat16(_ f: Float16) -> Float16 { return f }
|
|
|
|
// Basic sanity checks:
|
|
let f = 100.125 as Float16
|
|
expectEqual("f = 100.1", "f = \(f)")
|
|
|
|
expectDescription("0.0", asFloat16(0.0))
|
|
expectDescription("-0.0", -asFloat16(0.0))
|
|
expectDescription("0.1", asFloat16(0.1))
|
|
expectDescription("-0.1", asFloat16(-0.1))
|
|
expectDescription("1.0", asFloat16(1.0))
|
|
expectDescription("-1.0", asFloat16(-1.0))
|
|
expectDescription("1.1", asFloat16(1.1))
|
|
expectDescription("100.1", asFloat16(100.125))
|
|
expectDescription("-100.1", asFloat16(-100.125))
|
|
|
|
// Standard special numbers:
|
|
expectDescription("inf", Float16.infinity)
|
|
expectDescription("-inf", -Float16.infinity)
|
|
expectDescription("3.14", Float16.pi)
|
|
expectDescription("65504.0", Float16.greatestFiniteMagnitude)
|
|
#if !arch(arm)
|
|
expectDescription("6e-08", Float16.leastNonzeroMagnitude)
|
|
#endif
|
|
expectDescription("6.104e-05", Float16.leastNormalMagnitude)
|
|
|
|
// Special cases for the underlying algorithms:
|
|
// Smallest Float16 that requires 5 digits to print accurately
|
|
expectDescription("0.00010014", Float16(bitPattern: 0x0690))
|
|
|
|
// NaNs require special care in testing:
|
|
// NaN is printed with additional detail to debugDescription, but not description
|
|
expectNaN("nan", Float16.nan)
|
|
expectNaN("nan(0xff)", Float16(nan: 255, signaling: false))
|
|
expectNaN("nan(0xff)", Float16(bitPattern: 0x7eff))
|
|
expectNaN("-nan", -Float16.nan)
|
|
expectNaN("-nan(0xff)", -Float16(nan: 255, signaling: false))
|
|
/*
|
|
// These fail on macOS x86_64, pass on iphonesimulator-i386, and
|
|
// probably behave in varying fashion on ARM32 and ARM64.
|
|
// So I'll just comment them out for now...
|
|
// Once we get real Float16 argument passing everywhere,
|
|
// these can be enabled again.
|
|
expectFailure {
|
|
expectNaN("snan", Float16.signalingNaN)
|
|
expectNaN("-snan", -Float16.signalingNaN)
|
|
expectNaN("snan(0xff)", Float16(nan: 255, signaling: true))
|
|
expectNaN("-snan(0xff)", -Float16(nan: 255, signaling: true))
|
|
expectNaN("snan(0xff)", Float16(bitPattern: 0x7dff))
|
|
}
|
|
*/
|
|
expectEqual("nan", Float16.signalingNaN.description)
|
|
expectEqual("nan", (-Float16.signalingNaN).description)
|
|
expectEqual("nan", Float16(nan: 255, signaling: true).description)
|
|
expectEqual("nan", (-Float16(nan: 255, signaling: true)).description)
|
|
expectEqual("nan", Float16(bitPattern: 0x7dff).description)
|
|
|
|
// Every power of 10 should print with only a single digit '1'
|
|
let leastPowerOfTen = -7
|
|
let greatestPowerOfTen = 4
|
|
for power in leastPowerOfTen ... greatestPowerOfTen {
|
|
let s: String
|
|
if power < -4 { // 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 = Float16(s)!
|
|
expectDescription(s, f)
|
|
}
|
|
|
|
// Powers of 2
|
|
expectDescription("6e-08", 0x1p-24 as Float16)
|
|
expectDescription("1e-07", 0x1p-23 as Float16)
|
|
expectDescription("2.4e-07", 0x1p-22 as Float16)
|
|
expectDescription("5e-07", 0x1p-21 as Float16)
|
|
expectDescription("9.5e-07", 0x1p-20 as Float16)
|
|
expectDescription("1.9e-06", 0x1p-19 as Float16)
|
|
expectDescription("3.8e-06", 0x1p-18 as Float16)
|
|
expectDescription("7.6e-06", 0x1p-17 as Float16)
|
|
expectDescription("1.526e-05", 0x1p-16 as Float16)
|
|
expectDescription("3.05e-05", 0x1p-15 as Float16)
|
|
expectDescription("6.104e-05", 0x1p-14 as Float16)
|
|
expectDescription("0.0001221", 0x1p-13 as Float16)
|
|
expectDescription("0.0002441", 0x1p-12 as Float16)
|
|
expectDescription("0.0004883", 0x1p-11 as Float16)
|
|
expectDescription("0.000977", 0x1p-10 as Float16)
|
|
expectDescription("0.001953", 0x1p-9 as Float16)
|
|
expectDescription("0.003906", 0x1p-8 as Float16)
|
|
expectDescription("0.007812", 0x1p-7 as Float16)
|
|
expectDescription("0.01563", 0x1p-6 as Float16)
|
|
expectDescription("0.03125", 0x1p-5 as Float16)
|
|
expectDescription("0.0625", 0x1p-4 as Float16)
|
|
expectDescription("0.125", 0x1p-3 as Float16)
|
|
expectDescription("0.25", 0x1p-2 as Float16)
|
|
expectDescription("0.5", 0x1p-1 as Float16)
|
|
expectDescription("1.0", 0x1p0 as Float16)
|
|
expectDescription("2.0", 0x1p1 as Float16)
|
|
expectDescription("4.0", 0x1p2 as Float16)
|
|
expectDescription("8.0", 0x1p3 as Float16)
|
|
expectDescription("16.0", 0x1p4 as Float16)
|
|
expectDescription("32.0", 0x1p5 as Float16)
|
|
expectDescription("64.0", 0x1p6 as Float16)
|
|
expectDescription("128.0", 0x1p7 as Float16)
|
|
expectDescription("256.0", 0x1p8 as Float16)
|
|
expectDescription("512.0", 0x1p9 as Float16)
|
|
expectDescription("1024.0", 0x1p10 as Float16)
|
|
// Float16 can represent all integers -2048...2048
|
|
// For Float,Double, we use decimal form to this point,
|
|
// then exponential, but Float16 is so short that we
|
|
// just use decimal for all integer values:
|
|
expectDescription("2048.0", Float16(1 << 11))
|
|
expectDescription("-2048.0", -Float16(1 << 11))
|
|
expectDescription("2050.0", Float16(1 << 11).nextUp)
|
|
expectDescription("-2050.0", -(Float16(1 << 11).nextUp))
|
|
expectDescription("4096.0", 0x1p12 as Float16)
|
|
expectDescription("8192.0", 0x1p13 as Float16)
|
|
expectDescription("16384.0", 0x1p14 as Float16)
|
|
expectDescription("32768.0", 0x1p15 as Float16)
|
|
// Maximum Float16: 2**16 - 2**5
|
|
expectDescription("65504.0", Float16(bitPattern:0x7bff))
|
|
expectDescription("-65504.0", Float16(bitPattern:0xfbff))
|
|
|
|
expectDescription("1.0", asFloat16(1.00001))
|
|
expectDescription("12496.0", asFloat16(12500.0))
|
|
expectDescription("1250.0", asFloat16(1250.0))
|
|
expectDescription("125.0", asFloat16(125.0))
|
|
expectDescription("12.5", asFloat16(12.5))
|
|
expectDescription("1.25", asFloat16(1.25))
|
|
expectDescription("0.125", asFloat16(0.125))
|
|
expectDescription("0.0125", asFloat16(0.0125))
|
|
expectDescription("0.00125", asFloat16(0.00125))
|
|
expectDescription("0.000125", asFloat16(0.000125))
|
|
expectDescription("1.25e-05", asFloat16(0.0000125))
|
|
expectDescription("1.25e-06", asFloat16(0.00000125))
|
|
expectDescription("1e-07", asFloat16(0.000000125))
|
|
expectDescription("0.0", asFloat16(0.0000000125))
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
#if !arch(arm)
|
|
expectDescription("1e-45", Float.leastNonzeroMagnitude)
|
|
#endif
|
|
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", Float("7.038531e-26")!)
|
|
// Note: The above test computes the reference value from a
|
|
// string because `7.038531e-26 as Float` is broken:
|
|
// See https://bugs.swift.org/browse/SR-7124
|
|
|
|
// 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))
|
|
expectNaN("-nan", -Float.nan)
|
|
expectNaN("-nan(0xffff)", -Float(nan: 65535, signaling: false))
|
|
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))
|
|
|
|
// Every power of 10 should print with only a single digit '1'
|
|
#if arch(arm)
|
|
let lowerBound = -37
|
|
#else
|
|
let lowerBound = -45
|
|
#endif
|
|
for power in lowerBound ... 38 {
|
|
let s: String
|
|
if power < -4 || power > 7 { // 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)
|
|
}
|
|
|
|
// Float can represent all integers -(2^24)...(2^24)
|
|
let maxDecimalForm = Float(1 << 24)
|
|
expectDescription("16777216.0", maxDecimalForm)
|
|
expectDescription("-16777216.0", -maxDecimalForm)
|
|
// Outside of that range, use exponential form
|
|
expectDescription("1.6777218e+07", maxDecimalForm.nextUp)
|
|
expectDescription("-1.6777218e+07", -maxDecimalForm.nextUp)
|
|
|
|
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("12500000.0", asFloat32(12500000.0))
|
|
expectDescription("1250000.0", 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)
|
|
#if !arch(arm)
|
|
expectDescription("5e-324", Double.leastNonzeroMagnitude)
|
|
#endif
|
|
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)
|
|
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))
|
|
|
|
// We know how every power of 10 should print
|
|
#if arch(arm)
|
|
let lowerBound = -307
|
|
#else
|
|
let lowerBound = -323
|
|
#endif
|
|
for power in lowerBound ... 308 {
|
|
let s: String
|
|
if power < -4 || power > 15 { // 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)
|
|
}
|
|
}
|
|
|
|
// Double can represent all integers -(2^53)...(2^53)
|
|
let maxDecimalForm = Double((1 as Int64) << 53)
|
|
expectDescription("9007199254740992.0", maxDecimalForm)
|
|
expectDescription("-9007199254740992.0", -maxDecimalForm)
|
|
// Outside of that range, we use exponential form:
|
|
expectDescription("9.007199254740994e+15", maxDecimalForm.nextUp)
|
|
expectDescription("-9.007199254740994e+15", -maxDecimalForm.nextUp)
|
|
|
|
expectDescription("1.00000000000001", asFloat64(1.00000000000001))
|
|
expectDescription("1.25e+17", asFloat64(125000000000000000.0))
|
|
expectDescription("1.25e+16", asFloat64(12500000000000000.0))
|
|
expectDescription("1250000000000000.0", 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 > 19 { // 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)
|
|
}
|
|
}
|
|
|
|
// Float80 can represent all integers -(2^64)...(2^64):
|
|
let maxDecimalForm = Float80(UInt64.max) + 1.0
|
|
expectDescription("18446744073709551616.0", maxDecimalForm)
|
|
expectDescription("-18446744073709551616.0", -maxDecimalForm)
|
|
// Outside of that range, use exponential form
|
|
expectDescription("1.8446744073709551618e+19", maxDecimalForm.nextUp)
|
|
expectDescription("-1.8446744073709551618e+19", -maxDecimalForm.nextUp)
|
|
|
|
expectDescription("1.00000000000000001", asFloat80(1.00000000000000001))
|
|
expectDescription("1.25e+21", asFloat80(1250000000000000000000.0))
|
|
expectDescription("1.25e+20", asFloat80(125000000000000000000.0))
|
|
expectDescription("12500000000000000000.0", asFloat80(12500000000000000000.0))
|
|
expectDescription("1250000000000000000.0", 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()
|