Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base Library Adjustments for Latest Motoko RTS #589

Open
wants to merge 27 commits into
base: next-moc
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f048519
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Dec 19, 2022
f9c141c
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Dec 20, 2022
e3db654
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Dec 22, 2022
daffba5
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Jan 5, 2023
fc5c20f
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Jan 5, 2023
3a1da3b
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Jan 23, 2023
2779e0b
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Feb 10, 2023
ca4ada8
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Feb 13, 2023
88deb57
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Feb 20, 2023
ea36588
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Feb 28, 2023
3a361fb
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Aug 18, 2023
d09a2d9
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Aug 21, 2023
4f81659
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Aug 24, 2023
ff08bad
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Sep 19, 2023
f0d36aa
Adjust for Wasm64
luc-blaeser Sep 19, 2023
1514b30
Support more Float text format cases
luc-blaeser Sep 19, 2023
7402aba
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Dec 21, 2023
f59caac
Merge branch 'master' into luc/wasm64
luc-blaeser Dec 21, 2023
7c3fc9c
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Jan 31, 2024
cef9fc5
Merge branch 'master' into luc/wasm64
luc-blaeser Feb 13, 2024
41ded18
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser Mar 8, 2024
6285f0c
Merge branch 'master' into luc/wasm64
luc-blaeser Mar 8, 2024
f5c15e8
Merge branch 'master' of https://github.com/dfinity/motoko-base
luc-blaeser May 3, 2024
1aa2b86
Merge branch 'master' into luc/wasm64
luc-blaeser May 3, 2024
f33777b
Merge branch 'master' into luc/wasm64
luc-blaeser Aug 14, 2024
a391ab0
Adjust Float formatter for new Motoko RTS
luc-blaeser Aug 29, 2024
1bece92
Adjust change log
luc-blaeser Sep 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## Next release

* Breaking change (minor): `Float.format(#hex)` is no longer supported.
This is because newer versions of Motoko (such as with enhanced orthogonal persistence)
rely on the Rust-native formatter that does not offer this functionality.
It is expected that this formatter is very rarely used in practice.

* Formatter change (minor): The text formatting of `NaN`, positive or negative,
will be `NaN` in newer Motoko versions, while it was `nan` or `-nan` in older versions.

## 0.12.1

* Add `Iter.concat` function (thanks to AndyGura) (#650).
Expand Down
13 changes: 7 additions & 6 deletions src/Float.mo
Original file line number Diff line number Diff line change
Expand Up @@ -402,25 +402,26 @@ module {
/// * `#fix prec` as fixed-point format with `prec` digits
/// * `#exp prec` as exponential format with `prec` digits
/// * `#gen prec` as generic format with `prec` digits
/// * `#hex prec` as hexadecimal format with `prec` digits
/// * `#exact` as exact format that can be decoded without loss.
///
/// `-0.0` is formatted with negative sign bit.
/// Positive infinity is formatted as `inf`.
/// Negative infinity is formatted as `-inf`.
/// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit.
/// Positive infinity is formatted as "inf".
/// Negative infinity is formatted as "-inf".
///
/// Note: The numerical precision and the text format can vary between
/// Motoko versions and runtime configuration. Moreover, `NaN` can be printed
/// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign.
///
/// Example:
/// ```motoko
/// import Float "mo:base/Float";
///
/// Float.format(#exp 3, 123.0) // => "1.230e+02"
/// ```
public func format(fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #hex : Nat8; #exact }, x : Float) : Text = switch fmt {
public func format(fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #exact }, x : Float) : Text = switch fmt {
case (#fix(prec)) { Prim.floatToFormattedText(x, prec, 0) };
case (#exp(prec)) { Prim.floatToFormattedText(x, prec, 1) };
case (#gen(prec)) { Prim.floatToFormattedText(x, prec, 2) };
case (#hex(prec)) { Prim.floatToFormattedText(x, prec, 3) };
case (#exact) { Prim.floatToFormattedText(x, 17, 2) }
};

Expand Down
134 changes: 66 additions & 68 deletions test/Float.test.mo
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import Debug "../src/Debug";
import Float "../src/Float";
import Text "../src/Text";

import Suite "mo:matchers/Suite";
import T "mo:matchers/Testable";
Expand Down Expand Up @@ -44,11 +45,11 @@ let positiveNaN = Float.copySign(0.0 / 0.0, 1.0);
let negativeNaN = Float.copySign(0.0 / 0.0, -1.0);

func isPositiveNaN(number : Float) : Bool {
debug_show (number) == "nan"
Float.isNaN(number) and Float.copySign(1.0, number) == 1.0
};

func isNegativeNaN(number : Float) : Bool {
debug_show (number) == "-nan"
Float.isNaN(number) and Float.copySign(1.0, number) == -1.0
};

let positiveZero = 0.0;
Expand Down Expand Up @@ -87,7 +88,7 @@ let smallEpsilon = 1e-6;

class NaNMatcher() : M.Matcher<Float> {
public func describeMismatch(number : Float, _description : M.Description) {
Debug.print(debug_show (number) # " should be 'nan' or '-nan'")
Debug.print(debug_show (number) # " should be 'NaN' or '-NaN'")
};

public func matches(number : Float) : Bool {
Expand All @@ -97,7 +98,7 @@ class NaNMatcher() : M.Matcher<Float> {

class PositiveNaNMatcher() : M.Matcher<Float> {
public func describeMismatch(number : Float, _description : M.Description) {
Debug.print(debug_show (number) # " should be 'nan' (positive)")
Debug.print(debug_show (number) # " should be 'NaN' (positive)")
};

public func matches(number : Float) : Bool {
Expand All @@ -107,14 +108,49 @@ class PositiveNaNMatcher() : M.Matcher<Float> {

class NegativeNaNMatcher() : M.Matcher<Float> {
public func describeMismatch(number : Float, _description : M.Description) {
Debug.print(debug_show (number) # " should be '-nan' (negative)")
Debug.print(debug_show (number) # " should be '-NaN' (negative)")
};

public func matches(number : Float) : Bool {
isNegativeNaN(number)
}
};

// The Rust float formatter prints `NaN`.
// The Musl float formatter prints `nan`.
class PositiveNaNTextMatcher() : M.Matcher<Text> {
public func describeMismatch(text : Text, _description : M.Description) {
Debug.print("'" # text # "' should be 'NaN' or 'nan', depending on the Motoko version and runtime configuration")
};

public func matches(text : Text) : Bool {
text == "NaN" or text == "nan"
}
};

// The Rust float formatter ignores the sign of NaN and emits `NaN`.
// The Musl float formatter prints `-nan`.
class NegativeNaNTextMatcher() : M.Matcher<Text> {
public func describeMismatch(text : Text, _description : M.Description) {
Debug.print("'" # text # "' should be 'NaN' or '-nan', depending on the Motoko version and runtime configuration")
};

public func matches(text : Text) : Bool {
text == "NaN" or text == "-nan"
}
};

// Account for different numerical errors becoming visible in float formatting.
class TextPrefixMatcher(prefix : Text) : M.Matcher<Text> {
public func describeMismatch(text : Text, _description : M.Description) {
Debug.print("'" # text # "' does not start with '" # prefix # "'")
};

public func matches(text : Text) : Bool {
Text.startsWith(text, #text prefix)
}
};

// Some tests are adopted from Motoko compiler test `float-ops.mo`.

/* --------------------------------------- */
Expand Down Expand Up @@ -1341,7 +1377,7 @@ run(
test(
"one",
Float.exp(1.0),
M.equals(FloatTestable(Float.e, noEpsilon))
M.equals(FloatTestable(Float.e, smallEpsilon))
),
test(
"positive infinity",
Expand Down Expand Up @@ -1431,22 +1467,22 @@ run(
test(
"exact positive",
Float.format(#exact, 20.12345678901),
M.equals(T.text("20.12345678901"))
TextPrefixMatcher("20.1234567890")
),
test(
"exact negative",
Float.format(#exact, -20.12345678901),
M.equals(T.text("-20.12345678901"))
TextPrefixMatcher("-20.1234567890")
),
test(
"exact positive zero",
Float.format(#exact, positiveZero),
M.equals(T.text("0"))
M.anyOf([M.equals(T.text("0")), M.equals(T.text("0.00000000000000000"))])
),
test(
"exact negative zero",
Float.format(#exact, negativeZero),
M.equals(T.text("-0"))
M.anyOf([M.equals(T.text("-0")), M.equals(T.text("-0.00000000000000000"))])
),
test(
"exact positive infinity",
Expand All @@ -1461,12 +1497,12 @@ run(
test(
"exact positive NaN",
Float.format(#exact, positiveNaN),
M.equals(T.text("nan"))
PositiveNaNTextMatcher()
),
test(
"exact negative NaN",
Float.format(#exact, negativeNaN),
M.equals(T.text("-nan"))
NegativeNaNTextMatcher()
),
test(
"fix positive",
Expand Down Expand Up @@ -1501,32 +1537,32 @@ run(
test(
"fix positive NaN",
Float.format(#fix 6, positiveNaN),
M.equals(T.text("nan"))
PositiveNaNTextMatcher()
),
test(
"fix negative NaN",
Float.format(#fix 6, negativeNaN),
M.equals(T.text("-nan"))
NegativeNaNTextMatcher()
),
test(
"exp positive",
Float.format(#exp 9, 20.12345678901),
M.equals(T.text("2.012345679e+01"))
M.anyOf([M.equals(T.text("2.012345679e1")), M.equals(T.text("2.012345679e+01"))])
),
test(
"exp negative",
Float.format(#exp 9, -20.12345678901),
M.equals(T.text("-2.012345679e+01"))
M.anyOf([M.equals(T.text("-2.012345679e1")), M.equals(T.text("-2.012345679e+01"))])
),
test(
"exp positive zero",
Float.format(#exp 9, positiveZero),
M.equals(T.text("0.000000000e+00"))
M.anyOf([M.equals(T.text("0.000000000e0")), M.equals(T.text("0.000000000e+00"))])
),
test(
"exp negative zero",
Float.format(#exp 9, negativeZero),
M.equals(T.text("-0.000000000e+00"))
M.anyOf([M.equals(T.text("-0.000000000e0")), M.equals(T.text("-0.000000000e+00"))])
),
test(
"exp positive infinity",
Expand All @@ -1541,32 +1577,32 @@ run(
test(
"exp positive NaN",
Float.format(#exp 9, positiveNaN),
M.equals(T.text("nan"))
PositiveNaNTextMatcher()
),
test(
"exp negative NaN",
Float.format(#exp 9, negativeNaN),
M.equals(T.text("-nan"))
NegativeNaNTextMatcher()
),
test(
"gen positive",
Float.format(#gen 12, 20.12345678901),
M.equals(T.text("20.123456789"))
TextPrefixMatcher("20.123456789")
),
test(
"gen negative",
Float.format(#gen 12, -20.12345678901),
M.equals(T.text("-20.123456789"))
TextPrefixMatcher("-20.123456789")
),
test(
"gen positive zero",
Float.format(#gen 12, positiveZero),
M.equals(T.text("0"))
M.anyOf([M.equals(T.text("0")), M.equals(T.text("0.000000000000"))])
),
test(
"gen negative zero",
Float.format(#gen 12, negativeZero),
M.equals(T.text("-0"))
M.anyOf([M.equals(T.text("-0")), M.equals(T.text("-0.000000000000"))])
),
test(
"gen positive infinity",
Expand All @@ -1581,53 +1617,15 @@ run(
test(
"gen positive NaN",
Float.format(#gen 12, positiveNaN),
M.equals(T.text("nan"))
PositiveNaNTextMatcher()
),
test(
"gen negative NaN",
Float.format(#gen 12, negativeNaN),
M.equals(T.text("-nan"))
),
test(
"hex positive",
Float.format(#hex 10, 20.12345678901),
M.equals(T.text("0x1.41f9add374p+4"))
NegativeNaNTextMatcher()
),
test(
"hex negative",
Float.format(#hex 10, -20.12345678901),
M.equals(T.text("-0x1.41f9add374p+4"))
),
test(
"hex positive zero",
Float.format(#hex 10, positiveZero),
M.equals(T.text("0x0.0000000000p+0"))
),
test(
"hex negative zero",
Float.format(#hex 10, negativeZero),
M.equals(T.text("-0x0.0000000000p+0"))
),
test(
"hex positive infinity",
Float.format(#hex 10, positiveInfinity),
M.equals(T.text("inf"))
),
test(
"hex negative infinity",
Float.format(#hex 10, negativeInfinity),
M.equals(T.text("-inf"))
),
test(
"hex positive NaN",
Float.format(#hex 10, positiveNaN),
M.equals(T.text("nan"))
),
test(
"hex negative NaN",
Float.format(#hex 10, negativeNaN),
M.equals(T.text("-nan"))
)
// hex float formatting was only supported with Musl
// and is no longer supported with Rust-implemented formatter.
]
)
);
Expand Down Expand Up @@ -1671,12 +1669,12 @@ run(
test(
"positive NaN",
Float.toText(positiveNaN),
M.equals(T.text("nan"))
PositiveNaNTextMatcher()
),
test(
"negative NaN",
Float.toText(negativeNaN),
M.equals(T.text("-nan"))
NegativeNaNTextMatcher()
)
]
)
Expand Down
28 changes: 21 additions & 7 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@

STDLIB ?= ../src
MOC ?= moc
WASMTIME_OPTIONS = -C cache=n -W nan-canonicalization=y -W multi-memory -W bulk-memory
WASMTIME_OPTIONS = -C cache=n -W nan-canonicalization=y -W memory64 -W multi-memory -W bulk-memory

OUTDIR=_out

TESTS = $(wildcard *.mo)

TEST_TARGETS = $(patsubst %.mo,_out/%.checked,$(TESTS))
TEST_CLASSICAL_TARGETS = $(patsubst %.mo,_out/%.classical.checked,$(TESTS))
TEST_ENHANCED_TARGETS = $(patsubst %.mo,_out/%.enhanced.checked,$(TESTS))

all: $(OUTDIR)/import_all.checked $(TEST_TARGETS)
all: $(OUTDIR)/import_all.classical.checked \
$(OUTDIR)/import_all.enhanced.checked \
$(TEST_CLASSICAL_TARGETS) \
$(TEST_ENHANCED_TARGETS)

STDLIB_FILES= $(wildcard $(STDLIB)/*.mo)

VESSEL_PKGS= $(shell vessel sources)

MOC_COMMON_FLAGS=-c --package base $(STDLIB) $(VESSEL_PKGS) -wasi-system-api
MOC_CLASSICAL=$(MOC) $(MOC_COMMON_FLAGS)
MOC_ENHANCED=$(MOC) $(MOC_COMMON_FLAGS) --enhanced-orthogonal-persistence

$(OUTDIR):
@mkdir $@

Expand All @@ -25,11 +33,17 @@ $(OUTDIR)/import_all.mo: $(STDLIB_FILES) | $(OUTDIR)
echo "import _Import_$$f \"mo:base/$$f\";" >> $@; \
done

$(OUTDIR)/%.wasm: %.mo | $(OUTDIR)
$(MOC) -c --package base $(STDLIB) $(VESSEL_PKGS) -wasi-system-api -o $@ $<
$(OUTDIR)/%.classical.wasm: %.mo | $(OUTDIR)
$(MOC_CLASSICAL) -o $@ $<

$(OUTDIR)/%.enhanced.wasm: %.mo | $(OUTDIR)
$(MOC_ENHANCED) -o $@ $<

$(OUTDIR)/import_all.classical.wasm: $(OUTDIR)/import_all.mo | $(OUTDIR)
$(MOC_CLASSICAL) -o $@ $<

$(OUTDIR)/%.wasm: $(OUTDIR)/%.mo | $(OUTDIR)
$(MOC) -c --package base $(STDLIB) $(VESSEL_PKGS) -wasi-system-api -o $@ $<
$(OUTDIR)/import_all.enhanced.wasm: $(OUTDIR)/import_all.mo | $(OUTDIR)
$(MOC_ENHANCED) -o $@ $<

$(OUTDIR)/%.checked: $(OUTDIR)/%.wasm
wasmtime run $(WASMTIME_OPTIONS) $<
Expand Down