Skip to content

Commit

Permalink
Added constant pi and several math functions (#1187)
Browse files Browse the repository at this point in the history
Add constant pi and several math functions

Added:
* pi (up to IEEE 64-bit binary float precision)
* deg2rad, rad2deg
* atan2 (builtin using C/C++ std library)
* hypot (builtin using C/C++ std library)
* log2, log10
  • Loading branch information
rben01 authored Jan 18, 2025
1 parent 2b0122c commit 4487bfb
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 17 deletions.
4 changes: 3 additions & 1 deletion core/desugarer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct BuiltinDecl {
std::vector<UString> params;
};

static unsigned long max_builtin = 38;
static unsigned long max_builtin = 40;
BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
{
switch (builtin) {
Expand Down Expand Up @@ -79,6 +79,8 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
case 36: return {U"parseYaml", {U"str"}};
case 37: return {U"encodeUTF8", {U"str"}};
case 38: return {U"decodeUTF8", {U"arr"}};
case 39: return {U"atan2", {U"y", U"x"}};
case 40: return {U"hypot", {U"a", U"b"}};
default:
std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl;
std::abort();
Expand Down
16 changes: 16 additions & 0 deletions core/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,8 @@ class Interpreter {
builtins["parseYaml"] = &Interpreter::builtinParseYaml;
builtins["encodeUTF8"] = &Interpreter::builtinEncodeUTF8;
builtins["decodeUTF8"] = &Interpreter::builtinDecodeUTF8;
builtins["atan2"] = &Interpreter::builtinAtan2;
builtins["hypot"] = &Interpreter::builtinHypot;

DesugaredObject *stdlib = makeStdlibAST(alloc, "__internal__");
jsonnet_static_analysis(stdlib);
Expand Down Expand Up @@ -1099,6 +1101,20 @@ class Interpreter {
return nullptr;
}

const AST *builtinAtan2(const LocationRange &loc, const std::vector<Value> &args)
{
validateBuiltinArgs(loc, "atan2", args, {Value::NUMBER, Value::NUMBER});
scratch = makeNumberCheck(loc, std::atan2(args[0].v.d, args[1].v.d));
return nullptr;
}

const AST *builtinHypot(const LocationRange &loc, const std::vector<Value> &args)
{
validateBuiltinArgs(loc, "hypot", args, {Value::NUMBER, Value::NUMBER});
scratch = makeNumberCheck(loc, std::hypot(args[0].v.d, args[1].v.d));
return nullptr;
}

const AST *builtinType(const LocationRange &, const std::vector<Value> &args)
{
switch (args[0].t) {
Expand Down
9 changes: 9 additions & 0 deletions doc/_stdlib_gen/stdlib-content.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ local html = import 'html.libsonnet';
<ul><code>std.pow(x, n)</code></ul>
<ul><code>std.exp(x)</code></ul>
<ul><code>std.log(x)</code></ul>
<ul><code>std.log2(x)</code></ul>
<ul><code>std.log10(x)</code></ul>
<ul><code>std.exponent(x)</code></ul>
<ul><code>std.mantissa(x)</code></ul>
<ul><code>std.floor(x)</code></ul>
Expand All @@ -109,12 +111,19 @@ local html = import 'html.libsonnet';
<ul><code>std.asin(x)</code></ul>
<ul><code>std.acos(x)</code></ul>
<ul><code>std.atan(x)</code></ul>
<ul><code>std.atan2(y, x)</code></ul>
<ul><code>std.deg2rad(x)</code></ul>
<ul><code>std.rad2deg(x)</code></ul>
<ul><code>std.hypot(a, b)</code></ul>
<ul><code>std.round(x)</code></ul>
<ul><code>std.isEven(x)</code></ul>
<ul><code>std.isOdd(x)</code></ul>
<ul><code>std.isInteger(x)</code></ul>
<ul><code>std.isDecimal(x)</code></ul>
</ul>
<p>
The constant <code>std.pi</code> is also available.
</p>
<p>
The function <code>std.mod(a, b)</code> is what the % operator is desugared to. It performs
modulo arithmetic if the left hand side is a number, or if the left hand side is a string,
Expand Down
3 changes: 3 additions & 0 deletions doc/ref/spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -2825,7 +2825,10 @@ <h4 id="execution">Execution</h4>
<code>std.asin(x)</code>,
<code>std.acos(x)</code>,
<code>std.atan(x)</code>,
<code>std.atan2(y, x)</code>,
<code>std.log(x)</code>,
<code>std.log2(x)</code>,
<code>std.log10(x)</code>,
<code>std.exp(x)</code>,
<code>std.mantissa(x)</code>,
<code>std.exponent(x)</code> and
Expand Down
5 changes: 5 additions & 0 deletions doc/ref/stdlib.html
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ <h3 id="math">
<ul><code>std.pow(x, n)</code></ul>
<ul><code>std.exp(x)</code></ul>
<ul><code>std.log(x)</code></ul>
<ul><code>std.log2(x)</code></ul>
<ul><code>std.log10(x)</code></ul>
<ul><code>std.exponent(x)</code></ul>
<ul><code>std.mantissa(x)</code></ul>
<ul><code>std.floor(x)</code></ul>
Expand All @@ -245,6 +247,9 @@ <h3 id="math">
<ul><code>std.asin(x)</code></ul>
<ul><code>std.acos(x)</code></ul>
<ul><code>std.atan(x)</code></ul>
<ul><code>std.atan2(y, x)</code></ul>
<ul><code>std.deg2rad(x)</code></ul>
<ul><code>std.rad2deg(x)</code></ul>
<ul><code>std.round(x)</code></ul>
<ul><code>std.isEven(x)</code></ul>
<ul><code>std.isOdd(x)</code></ul>
Expand Down
9 changes: 9 additions & 0 deletions stdlib/std.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,15 @@ limitations under the License.
else
error 'Operator % cannot be used on types ' + std.type(a) + ' and ' + std.type(b) + '.',

// this is the most precision that will fit in a f64
pi:: 3.14159265358979311600,

deg2rad(x):: x * std.pi / 180,
rad2deg(x):: x * 180 / std.pi,

log2(x):: std.log(x) / std.log(2),
log10(x):: std.log(x) / std.log(10),

map(func, arr)::
if !std.isFunction(func) then
error ('std.map first param must be function, got ' + std.type(func))
Expand Down
48 changes: 32 additions & 16 deletions test_suite/stdlib.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -56,31 +56,47 @@ std.assertEqual(std.abs(33), 33) &&
std.assertEqual(std.abs(-33), 33) &&
std.assertEqual(std.abs(0), 0) &&

// Ordinary (non-test) code can define pi as 2*std.acos(0)
local pi = 3.14159265359;

assertClose(std.sin(0.0 * pi), 0) &&
assertClose(std.sin(0.5 * pi), 1) &&
assertClose(std.sin(1.0 * pi), 0) &&
assertClose(std.sin(1.5 * pi), -1) &&
assertClose(std.sin(2.0 * pi), 0) &&
assertClose(std.cos(0.0 * pi), 1) &&
assertClose(std.cos(0.5 * pi), 0) &&
assertClose(std.cos(1.0 * pi), -1) &&
assertClose(std.cos(1.5 * pi), 0) &&
assertClose(std.cos(2.0 * pi), 1) &&
assertClose(std.sin(0.0 * std.pi), 0) &&
assertClose(std.sin(0.5 * std.pi), 1) &&
assertClose(std.sin(1.0 * std.pi), 0) &&
assertClose(std.sin(1.5 * std.pi), -1) &&
assertClose(std.sin(2.0 * std.pi), 0) &&
assertClose(std.cos(0.0 * std.pi), 1) &&
assertClose(std.cos(0.5 * std.pi), 0) &&
assertClose(std.cos(1.0 * std.pi), -1) &&
assertClose(std.cos(1.5 * std.pi), 0) &&
assertClose(std.cos(2.0 * std.pi), 1) &&
assertClose(std.tan(0), 0) &&
assertClose(std.tan(0.25 * pi), 1) &&
assertClose(std.tan(0.25 * std.pi), 1) &&
assertClose(std.asin(0), 0) &&
assertClose(std.acos(1), 0) &&
assertClose(std.asin(1), 0.5 * pi) &&
assertClose(std.acos(0), 0.5 * pi) &&
assertClose(std.asin(1), 0.5 * std.pi) &&
assertClose(std.acos(0), 0.5 * std.pi) &&
assertClose(std.atan(0), 0) &&
assertClose(std.atan2(1, 1), std.pi / 4) &&
assertClose(std.atan2(-1, 1), -std.pi / 4) &&
assertClose(std.atan2(1.2, -3.8), 2.835713782184941) && // arbitrary, done on a calculator
assertClose(std.deg2rad(0), 0) &&
assertClose(std.deg2rad(45), std.pi / 4) &&
assertClose(std.deg2rad(90), std.pi / 2) &&
assertClose(std.deg2rad(172), 3.0019663134302466) && // arbitrary, done on a calculator
assertClose(std.rad2deg(std.pi / 4), 45) &&
assertClose(std.rad2deg(std.pi / 2), 90) &&
assertClose(std.rad2deg(3.0019663134302466), 172) && // arbitrary, done on a calculator
assertClose(std.hypot(3, 4), 5) &&
assertClose(std.hypot(5, 12), 13) &&
assertClose(std.hypot(1, 1), std.sqrt(2)) &&
assertClose(std.log(std.exp(5)), 5) &&
assertClose(std.mantissa(1), 0.5) &&
assertClose(std.exponent(1), 1) &&
assertClose(std.mantissa(128), 0.5) &&
assertClose(std.exponent(128), 8) &&
assertClose(std.log2(std.pow(2, -5)), -5) &&
assertClose(std.log2(std.pow(2, 0)), 0) &&
assertClose(std.log2(std.pow(2, std.pi)), std.pi) &&
assertClose(std.log10(std.pow(10, -5)), -5) &&
assertClose(std.log10(std.pow(10, 0)), 0) &&
assertClose(std.log10(std.pow(10, std.pi)), std.pi) &&

std.assertEqual(std.clamp(-3, 0, 5), 0) &&
std.assertEqual(std.clamp(4, 0, 5), 4) &&
Expand Down

0 comments on commit 4487bfb

Please sign in to comment.