1 module toml_foolery.encode.types.floating_point; 2 3 import std.math : modf, isNaN; 4 import std.traits : isFloatingPoint; 5 6 import toml_foolery.encode; 7 8 9 package(toml_foolery.encode) enum bool makesTomlFloat(T) = ( 10 isFloatingPoint!T 11 ); 12 13 /// Serializes float, double, and real into TOML floating point values. 14 /// TOML floats are always 64-bit, floats and reals are converted to doubles 15 /// first. 16 package(toml_foolery.encode) void tomlifyValueImpl(T)( 17 const T value, 18 ref Appender!string buffer, 19 immutable string[] parentTables 20 ) 21 if (makesTomlFloat!T) 22 { 23 if (value == T(0.0)) 24 { 25 buffer.put("0.0"); 26 } 27 else if (value == T.infinity) 28 { 29 buffer.put("inf"); 30 } 31 else if (value == -T.infinity) 32 { 33 buffer.put("-inf"); 34 } 35 else if (value.isNaN) 36 { 37 buffer.put("nan"); 38 } 39 else 40 { 41 real integralPartF; 42 real fractionalPart = modf(value.to!real, integralPartF); 43 size_t fracPartLength = fractionalPart.to!string.length; 44 size_t decimals = 45 fracPartLength >= 3 ? 46 fracPartLength - 2 : 47 1; 48 49 buffer.put("%,?.*f".format(decimals, '_', value)); 50 } 51 } 52 53 version(unittest) 54 { 55 import std.meta : AliasSeq; 56 static foreach (type; AliasSeq!(float, double, real)) 57 { 58 mixin(fpTest!type); 59 } 60 } 61 62 // I tried using a mixin template for this, it compiled but the tests didn't 63 // show up in the output. 64 private string fpTest(T)() 65 if (isFloatingPoint!T) 66 { 67 return 68 ` 69 @("Encode ` ~ "`" ~ T.stringof ~ "`" ~ ` values — non-weird") 70 unittest 71 { 72 ` ~ T.stringof ~ ` zero = ` ~ T.stringof ~ `(0.0f); 73 expect(_tomlifyValue(zero)).toEqual("0.0"); 74 75 ` ~ T.stringof ~ ` one = ` ~ T.stringof ~ `(1.0f); 76 expect(_tomlifyValue(one)).toEqual("1.0"); 77 78 ` ~ T.stringof ~ ` negOne = ` ~ T.stringof ~ `(-1.0f); 79 expect(_tomlifyValue(negOne)).toEqual("-1.0"); 80 81 ` ~ T.stringof ~ ` po2 = ` ~ T.stringof ~ `(512.5); 82 expect(_tomlifyValue(po2)).toEqual("512.5"); 83 } 84 85 @("Encode ` ~ "`" ~ T.stringof ~ "`" ~ ` values — weird") 86 unittest 87 { 88 // Negative zero and negative NaN are not supported. 89 // They just become 0 and NaN. 90 91 ` ~ T.stringof ~ ` negZero = ` ~ T.stringof ~ `(-0.0f); 92 expect(_tomlifyValue(negZero)).toEqual("0.0"); 93 94 ` ~ T.stringof ~ ` posInf = ` ~ T.stringof ~ `(` ~ T.stringof ~ `.infinity); 95 expect(_tomlifyValue(posInf)).toEqual("inf"); 96 97 ` ~ T.stringof ~ ` negInf = ` ~ T.stringof ~ `(-` ~ T.stringof ~ `.infinity); 98 expect(_tomlifyValue(negInf)).toEqual("-inf"); 99 100 ` ~ T.stringof ~ ` posNan = ` ~ T.stringof ~ `(` ~ T.stringof ~ `.nan); 101 expect(_tomlifyValue(posNan)).toEqual("nan"); 102 103 ` ~ T.stringof ~ ` negNan = ` ~ T.stringof ~ `(-` ~ T.stringof ~ `.nan); 104 expect(_tomlifyValue(negNan)).toEqual("nan"); 105 } 106 ` 107 ; 108 }