1use std::cmp;
4use std::cmp::Ordering;
5
6use az::SaturatingAs;
7use typst_syntax::{Span, Spanned};
8use typst_utils::{round_int_with_precision, round_with_precision};
9
10use crate::diag::{At, HintedString, SourceResult, StrResult, bail};
11use crate::foundations::{Decimal, IntoValue, Module, Scope, Value, cast, func, ops};
12use crate::layout::{Angle, Fr, Length, Ratio};
13
14pub fn module() -> Module {
16 let mut scope = Scope::new();
17 scope.define_func::<abs>();
18 scope.define_func::<pow>();
19 scope.define_func::<exp>();
20 scope.define_func::<sqrt>();
21 scope.define_func::<root>();
22 scope.define_func::<sin>();
23 scope.define_func::<cos>();
24 scope.define_func::<tan>();
25 scope.define_func::<asin>();
26 scope.define_func::<acos>();
27 scope.define_func::<atan>();
28 scope.define_func::<atan2>();
29 scope.define_func::<sinh>();
30 scope.define_func::<cosh>();
31 scope.define_func::<tanh>();
32 scope.define_func::<log>();
33 scope.define_func::<ln>();
34 scope.define_func::<fact>();
35 scope.define_func::<perm>();
36 scope.define_func::<binom>();
37 scope.define_func::<gcd>();
38 scope.define_func::<lcm>();
39 scope.define_func::<floor>();
40 scope.define_func::<ceil>();
41 scope.define_func::<trunc>();
42 scope.define_func::<fract>();
43 scope.define_func::<round>();
44 scope.define_func::<clamp>();
45 scope.define_func::<min>();
46 scope.define_func::<max>();
47 scope.define_func::<even>();
48 scope.define_func::<odd>();
49 scope.define_func::<rem>();
50 scope.define_func::<div_euclid>();
51 scope.define_func::<rem_euclid>();
52 scope.define_func::<quo>();
53 scope.define_func::<norm>();
54 scope.define("inf", f64::INFINITY);
55 scope.define("pi", std::f64::consts::PI);
56 scope.define("tau", std::f64::consts::TAU);
57 scope.define("e", std::f64::consts::E);
58 Module::new("calc", scope)
59}
60
61#[func(title = "Absolute")]
70pub fn abs(
71 value: ToAbs,
73) -> Value {
74 value.0
75}
76
77pub struct ToAbs(Value);
79
80cast! {
81 ToAbs,
82 v: i64 => Self(v.abs().into_value()),
83 v: f64 => Self(v.abs().into_value()),
84 v: Length => Self(Value::Length(v.try_abs()
85 .ok_or("cannot take absolute value of this length")?)),
86 v: Angle => Self(Value::Angle(v.abs())),
87 v: Ratio => Self(Value::Ratio(v.abs())),
88 v: Fr => Self(Value::Fraction(v.abs())),
89 v: Decimal => Self(Value::Decimal(v.abs()))
90}
91
92#[func(title = "Power")]
99pub fn pow(
100 span: Span,
101 base: DecNum,
105 exponent: Spanned<Num>,
107) -> SourceResult<DecNum> {
108 match exponent.v {
109 _ if exponent.v.float() == 0.0 && base.is_zero() => {
110 bail!(span, "zero to the power of zero is undefined")
111 }
112 Num::Int(i) if i32::try_from(i).is_err() => {
113 bail!(exponent.span, "exponent is too large")
114 }
115 Num::Float(f) if !f.is_normal() && f != 0.0 => {
116 bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN")
117 }
118 _ => {}
119 };
120
121 match (base, exponent.v) {
122 (DecNum::Int(a), Num::Int(b)) if b >= 0 => a
123 .checked_pow(b as u32)
124 .map(DecNum::Int)
125 .ok_or_else(too_large)
126 .at(span),
127 (DecNum::Decimal(a), Num::Int(b)) => {
128 a.checked_powi(b).map(DecNum::Decimal).ok_or_else(too_large).at(span)
129 }
130 (a, b) => {
131 let Some(a) = a.float() else {
132 return Err(cant_apply_to_decimal_and_float()).at(span);
133 };
134
135 let result = if a == std::f64::consts::E {
136 b.float().exp()
137 } else if a == 2.0 {
138 b.float().exp2()
139 } else if let Num::Int(b) = b {
140 a.powi(b as i32)
141 } else {
142 a.powf(b.float())
143 };
144
145 if result.is_nan() {
146 bail!(span, "the result is not a real number")
147 }
148
149 Ok(DecNum::Float(result))
150 }
151 }
152}
153
154#[func(title = "Exponential")]
160pub fn exp(
161 span: Span,
162 exponent: Spanned<Num>,
164) -> SourceResult<f64> {
165 match exponent.v {
166 Num::Int(i) if i32::try_from(i).is_err() => {
167 bail!(exponent.span, "exponent is too large")
168 }
169 Num::Float(f) if !f.is_normal() && f != 0.0 => {
170 bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN")
171 }
172 _ => {}
173 }
174
175 let result = exponent.v.float().exp();
176 if result.is_nan() {
177 bail!(span, "the result is not a real number")
178 }
179
180 Ok(result)
181}
182
183#[func(title = "Square Root")]
190pub fn sqrt(
191 value: Spanned<Num>,
193) -> SourceResult<f64> {
194 if value.v.float() < 0.0 {
195 bail!(value.span, "cannot take square root of negative number");
196 }
197 Ok(value.v.float().sqrt())
198}
199
200#[func]
209pub fn root(
210 radicand: f64,
212 index: Spanned<i64>,
214) -> SourceResult<f64> {
215 if index.v == 0 {
216 bail!(index.span, "cannot take the 0th root of a number");
217 } else if radicand < 0.0 {
218 if index.v % 2 == 0 {
219 bail!(
220 index.span,
221 "negative numbers do not have a real nth root when n is even"
222 );
223 } else {
224 Ok(-(-radicand).powf(1.0 / index.v as f64))
225 }
226 } else {
227 Ok(radicand.powf(1.0 / index.v as f64))
228 }
229}
230
231#[func(title = "Sine")]
241pub fn sin(
242 angle: AngleLike,
244) -> f64 {
245 match angle {
246 AngleLike::Angle(a) => a.sin(),
247 AngleLike::Int(n) => (n as f64).sin(),
248 AngleLike::Float(n) => n.sin(),
249 }
250}
251
252#[func(title = "Cosine")]
262pub fn cos(
263 angle: AngleLike,
265) -> f64 {
266 match angle {
267 AngleLike::Angle(a) => a.cos(),
268 AngleLike::Int(n) => (n as f64).cos(),
269 AngleLike::Float(n) => n.cos(),
270 }
271}
272
273#[func(title = "Tangent")]
283pub fn tan(
284 angle: AngleLike,
286) -> f64 {
287 match angle {
288 AngleLike::Angle(a) => a.tan(),
289 AngleLike::Int(n) => (n as f64).tan(),
290 AngleLike::Float(n) => n.tan(),
291 }
292}
293
294#[func(title = "Arcsine")]
301pub fn asin(
302 value: Spanned<Num>,
304) -> SourceResult<Angle> {
305 let val = value.v.float();
306 if val < -1.0 || val > 1.0 {
307 bail!(value.span, "value must be between -1 and 1");
308 }
309 Ok(Angle::rad(val.asin()))
310}
311
312#[func(title = "Arccosine")]
319pub fn acos(
320 value: Spanned<Num>,
322) -> SourceResult<Angle> {
323 let val = value.v.float();
324 if val < -1.0 || val > 1.0 {
325 bail!(value.span, "value must be between -1 and 1");
326 }
327 Ok(Angle::rad(val.acos()))
328}
329
330#[func(title = "Arctangent")]
337pub fn atan(
338 value: Num,
340) -> Angle {
341 Angle::rad(value.float().atan())
342}
343
344#[func(title = "Four-quadrant Arctangent")]
353pub fn atan2(
354 x: Num,
356 y: Num,
358) -> Angle {
359 Angle::rad(f64::atan2(y.float(), x.float()))
360}
361
362#[func(title = "Hyperbolic Sine")]
369pub fn sinh(
370 value: f64,
372) -> f64 {
373 value.sinh()
374}
375
376#[func(title = "Hyperbolic Cosine")]
383pub fn cosh(
384 value: f64,
386) -> f64 {
387 value.cosh()
388}
389
390#[func(title = "Hyperbolic Tangent")]
397pub fn tanh(
398 value: f64,
400) -> f64 {
401 value.tanh()
402}
403
404#[func(title = "Logarithm")]
412pub fn log(
413 span: Span,
414 value: Spanned<Num>,
416 #[named]
418 #[default(Spanned::new(10.0, Span::detached()))]
419 base: Spanned<f64>,
420) -> SourceResult<f64> {
421 let number = value.v.float();
422 if number <= 0.0 {
423 bail!(value.span, "value must be strictly positive")
424 }
425
426 if !base.v.is_normal() {
427 bail!(base.span, "base may not be zero, NaN, infinite, or subnormal")
428 }
429
430 let result = if base.v == std::f64::consts::E {
431 number.ln()
432 } else if base.v == 2.0 {
433 number.log2()
434 } else if base.v == 10.0 {
435 number.log10()
436 } else {
437 number.log(base.v)
438 };
439
440 if result.is_infinite() || result.is_nan() {
441 bail!(span, "the result is not a real number")
442 }
443
444 Ok(result)
445}
446
447#[func(title = "Natural Logarithm")]
453pub fn ln(
454 span: Span,
455 value: Spanned<Num>,
457) -> SourceResult<f64> {
458 let number = value.v.float();
459 if number <= 0.0 {
460 bail!(value.span, "value must be strictly positive")
461 }
462
463 let result = number.ln();
464 if result.is_infinite() {
465 bail!(span, "result close to -inf")
466 }
467
468 Ok(result)
469}
470
471#[func(title = "Factorial")]
477pub fn fact(
478 number: u64,
480) -> StrResult<i64> {
481 Ok(fact_impl(1, number).ok_or_else(too_large)?)
482}
483
484#[func(title = "Permutation")]
494pub fn perm(
495 base: u64,
497 numbers: u64,
499) -> StrResult<i64> {
500 if base < numbers {
502 return Ok(0);
503 }
504
505 Ok(fact_impl(base - numbers + 1, base).ok_or_else(too_large)?)
506}
507
508fn fact_impl(start: u64, end: u64) -> Option<i64> {
511 if end + 1 < start {
513 return Some(0);
514 }
515
516 let real_start: u64 = cmp::max(1, start);
517 let mut count: u64 = 1;
518 for i in real_start..=end {
519 count = count.checked_mul(i)?;
520 }
521
522 count.try_into().ok()
523}
524
525#[func(title = "Binomial")]
534pub fn binom(
535 n: u64,
537 k: u64,
539) -> StrResult<i64> {
540 Ok(binom_impl(n, k).ok_or_else(too_large)?)
541}
542
543fn binom_impl(n: u64, k: u64) -> Option<i64> {
547 if k > n {
548 return Some(0);
549 }
550
551 let real_k = cmp::min(n - k, k);
553 if real_k == 0 {
554 return Some(1);
555 }
556
557 let mut result: u64 = 1;
558 for i in 0..real_k {
559 result = result.checked_mul(n - i)?.checked_div(i + 1)?;
560 }
561
562 result.try_into().ok()
563}
564
565#[func(title = "Greatest Common Divisor")]
574pub fn gcd(
575 a: i64,
577 b: i64,
579) -> StrResult<i64> {
580 let (mut a, mut b) = (a, b);
581 while b != 0 {
582 let temp = b;
583 b = a.checked_rem(b).ok_or_else(too_large)?;
584 a = temp;
585 }
586
587 Ok(a.abs())
588}
589
590#[func(title = "Least Common Multiple")]
596pub fn lcm(
597 a: i64,
599 b: i64,
601) -> StrResult<i64> {
602 if a == b {
603 return Ok(a.abs());
604 }
605
606 Ok(a.checked_div(gcd(a, b)?)
607 .and_then(|gcd| gcd.checked_mul(b))
608 .map(|v| v.abs())
609 .ok_or_else(too_large)?)
610}
611
612#[func]
627pub fn floor(
628 value: DecNum,
630) -> StrResult<i64> {
631 match value {
632 DecNum::Int(n) => Ok(n),
633 DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.floor())
634 .map_err(|_| too_large())?),
635 DecNum::Decimal(n) => Ok(i64::try_from(n.floor()).map_err(|_| too_large())?),
636 }
637}
638
639#[func]
654pub fn ceil(
655 value: DecNum,
657) -> StrResult<i64> {
658 match value {
659 DecNum::Int(n) => Ok(n),
660 DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.ceil())
661 .map_err(|_| too_large())?),
662 DecNum::Decimal(n) => Ok(i64::try_from(n.ceil()).map_err(|_| too_large())?),
663 }
664}
665
666#[func(title = "Truncate")]
681pub fn trunc(
682 value: DecNum,
684) -> StrResult<i64> {
685 match value {
686 DecNum::Int(n) => Ok(n),
687 DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.trunc())
688 .map_err(|_| too_large())?),
689 DecNum::Decimal(n) => Ok(i64::try_from(n.trunc()).map_err(|_| too_large())?),
690 }
691}
692
693#[func(title = "Fractional")]
703pub fn fract(
704 value: DecNum,
706) -> DecNum {
707 match value {
708 DecNum::Int(_) => DecNum::Int(0),
709 DecNum::Float(n) => DecNum::Float(n.fract()),
710 DecNum::Decimal(n) => DecNum::Decimal(n.fract()),
711 }
712}
713
714#[func]
749pub fn round(
750 value: DecNum,
752 #[named]
757 #[default(0)]
758 digits: i64,
759) -> StrResult<DecNum> {
760 match value {
761 DecNum::Int(n) => Ok(DecNum::Int(
762 round_int_with_precision(n, digits.saturating_as::<i16>())
763 .ok_or_else(too_large)?,
764 )),
765 DecNum::Float(n) => {
766 Ok(DecNum::Float(round_with_precision(n, digits.saturating_as::<i16>())))
767 }
768 DecNum::Decimal(n) => Ok(DecNum::Decimal(
769 n.round(digits.saturating_as::<i32>()).ok_or_else(too_large)?,
770 )),
771 }
772}
773
774#[func]
784pub fn clamp(
785 span: Span,
786 value: DecNum,
788 min: DecNum,
790 max: Spanned<DecNum>,
792) -> SourceResult<DecNum> {
793 if min
797 .apply2(max.v, |min, max| max < min, |min, max| max < min, |min, max| max < min)
798 .unwrap_or(false)
799 {
800 bail!(max.span, "max must be greater than or equal to min")
801 }
802
803 value
804 .apply3(min, max.v, i64::clamp, f64::clamp, Decimal::clamp)
805 .ok_or_else(cant_apply_to_decimal_and_float)
806 .at(span)
807}
808
809#[func(title = "Minimum")]
816pub fn min(
817 span: Span,
818 #[variadic]
821 values: Vec<Spanned<Value>>,
822) -> SourceResult<Value> {
823 minmax(span, values, Ordering::Less)
824}
825
826#[func(title = "Maximum")]
833pub fn max(
834 span: Span,
835 #[variadic]
838 values: Vec<Spanned<Value>>,
839) -> SourceResult<Value> {
840 minmax(span, values, Ordering::Greater)
841}
842
843fn minmax(
845 span: Span,
846 values: Vec<Spanned<Value>>,
847 goal: Ordering,
848) -> SourceResult<Value> {
849 let mut iter = values.into_iter();
850 let Some(Spanned { v: mut extremum, .. }) = iter.next() else {
851 bail!(span, "expected at least one value");
852 };
853
854 for Spanned { v, span } in iter {
855 let ordering = ops::compare(&v, &extremum).at(span)?;
856 if ordering == goal {
857 extremum = v;
858 }
859 }
860
861 Ok(extremum)
862}
863
864#[func]
872pub fn even(
873 value: i64,
875) -> bool {
876 value % 2 == 0
877}
878
879#[func]
887pub fn odd(
888 value: i64,
890) -> bool {
891 value % 2 != 0
892}
893
894#[func(title = "Remainder")]
910pub fn rem(
911 span: Span,
912 dividend: DecNum,
914 divisor: Spanned<DecNum>,
916) -> SourceResult<DecNum> {
917 if divisor.v.is_zero() {
918 bail!(divisor.span, "divisor must not be zero");
919 }
920
921 dividend
922 .apply2(
923 divisor.v,
924 |a, b| Some(DecNum::Int(a.checked_rem(b).unwrap_or(0))),
927 |a, b| Some(DecNum::Float(a % b)),
928 |a, b| a.checked_rem(b).map(DecNum::Decimal),
929 )
930 .ok_or_else(cant_apply_to_decimal_and_float)
931 .at(span)?
932 .ok_or("dividend too small compared to divisor")
933 .at(span)
934}
935
936#[func(title = "Euclidean Division")]
954pub fn div_euclid(
955 span: Span,
956 dividend: DecNum,
958 divisor: Spanned<DecNum>,
960) -> SourceResult<DecNum> {
961 if divisor.v.is_zero() {
962 bail!(divisor.span, "divisor must not be zero");
963 }
964
965 dividend
966 .apply2(
967 divisor.v,
968 |a, b| a.checked_div_euclid(b).map(DecNum::Int),
969 |a, b| Some(DecNum::Float(a.div_euclid(b))),
970 |a, b| a.checked_div_euclid(b).map(DecNum::Decimal),
971 )
972 .ok_or_else(cant_apply_to_decimal_and_float)
973 .at(span)?
974 .ok_or_else(too_large)
975 .at(span)
976}
977
978#[func(title = "Euclidean Remainder", keywords = ["modulo", "modulus"])]
997pub fn rem_euclid(
998 span: Span,
999 dividend: DecNum,
1001 divisor: Spanned<DecNum>,
1003) -> SourceResult<DecNum> {
1004 if divisor.v.is_zero() {
1005 bail!(divisor.span, "divisor must not be zero");
1006 }
1007
1008 dividend
1009 .apply2(
1010 divisor.v,
1011 |a, b| Some(DecNum::Int(a.checked_rem_euclid(b).unwrap_or(0))),
1014 |a, b| Some(DecNum::Float(a.rem_euclid(b))),
1015 |a, b| a.checked_rem_euclid(b).map(DecNum::Decimal),
1016 )
1017 .ok_or_else(cant_apply_to_decimal_and_float)
1018 .at(span)?
1019 .ok_or("dividend too small compared to divisor")
1020 .at(span)
1021}
1022
1023#[func(title = "Quotient")]
1035pub fn quo(
1036 span: Span,
1037 dividend: DecNum,
1039 divisor: Spanned<DecNum>,
1041) -> SourceResult<i64> {
1042 if divisor.v.is_zero() {
1043 bail!(divisor.span, "divisor must not be zero");
1044 }
1045
1046 let divided = dividend
1047 .apply2(
1048 divisor.v,
1049 |a, b| a.checked_div(b).map(DecNum::Int),
1050 |a, b| Some(DecNum::Float(a / b)),
1051 |a, b| a.checked_div(b).map(DecNum::Decimal),
1052 )
1053 .ok_or_else(cant_apply_to_decimal_and_float)
1054 .at(span)?
1055 .ok_or_else(too_large)
1056 .at(span)?;
1057
1058 floor(divided).at(span)
1059}
1060
1061#[func(title = "𝑝-Norm")]
1068pub fn norm(
1069 #[named]
1071 #[default(Spanned::new(2.0, Span::detached()))]
1072 p: Spanned<f64>,
1073 #[variadic]
1076 values: Vec<f64>,
1077) -> SourceResult<f64> {
1078 if p.v <= 0.0 {
1079 bail!(p.span, "p must be greater than zero");
1080 }
1081
1082 let abs = values.into_iter().map(f64::abs);
1084
1085 Ok(if p.v.is_infinite() {
1086 abs.max_by(|a, b| a.total_cmp(b)).unwrap_or(0.0)
1088 } else {
1089 abs.map(|v| v.powf(p.v)).sum::<f64>().powf(1.0 / p.v)
1090 })
1091}
1092
1093#[derive(Debug, Copy, Clone)]
1095pub enum Num {
1096 Int(i64),
1097 Float(f64),
1098}
1099
1100impl Num {
1101 fn float(self) -> f64 {
1102 match self {
1103 Self::Int(v) => v as f64,
1104 Self::Float(v) => v,
1105 }
1106 }
1107}
1108
1109cast! {
1110 Num,
1111 self => match self {
1112 Self::Int(v) => v.into_value(),
1113 Self::Float(v) => v.into_value(),
1114 },
1115 v: i64 => Self::Int(v),
1116 v: f64 => Self::Float(v),
1117}
1118
1119#[derive(Debug, Copy, Clone)]
1122pub enum DecNum {
1123 Int(i64),
1124 Float(f64),
1125 Decimal(Decimal),
1126}
1127
1128impl DecNum {
1129 fn is_zero(self) -> bool {
1131 match self {
1132 Self::Int(i) => i == 0,
1133 Self::Float(f) => f == 0.0,
1134 Self::Decimal(d) => d.is_zero(),
1135 }
1136 }
1137
1138 fn float(self) -> Option<f64> {
1141 match self {
1142 Self::Int(i) => Some(i as f64),
1143 Self::Float(f) => Some(f),
1144 Self::Decimal(_) => None,
1145 }
1146 }
1147
1148 fn decimal(self) -> Option<Decimal> {
1151 match self {
1152 Self::Int(i) => Some(Decimal::from(i)),
1153 Self::Float(_) => None,
1154 Self::Decimal(d) => Some(d),
1155 }
1156 }
1157
1158 fn apply2<T>(
1162 self,
1163 other: Self,
1164 int: impl FnOnce(i64, i64) -> T,
1165 float: impl FnOnce(f64, f64) -> T,
1166 decimal: impl FnOnce(Decimal, Decimal) -> T,
1167 ) -> Option<T> {
1168 match (self, other) {
1169 (Self::Int(a), Self::Int(b)) => Some(int(a, b)),
1170 (Self::Decimal(a), Self::Decimal(b)) => Some(decimal(a, b)),
1171 (Self::Decimal(a), Self::Int(b)) => Some(decimal(a, Decimal::from(b))),
1172 (Self::Int(a), Self::Decimal(b)) => Some(decimal(Decimal::from(a), b)),
1173 (a, b) => Some(float(a.float()?, b.float()?)),
1174 }
1175 }
1176
1177 fn apply3(
1181 self,
1182 other: Self,
1183 third: Self,
1184 int: impl FnOnce(i64, i64, i64) -> i64,
1185 float: impl FnOnce(f64, f64, f64) -> f64,
1186 decimal: impl FnOnce(Decimal, Decimal, Decimal) -> Decimal,
1187 ) -> Option<Self> {
1188 match (self, other, third) {
1189 (Self::Int(a), Self::Int(b), Self::Int(c)) => Some(Self::Int(int(a, b, c))),
1190 (Self::Decimal(a), b, c) => {
1191 Some(Self::Decimal(decimal(a, b.decimal()?, c.decimal()?)))
1192 }
1193 (a, Self::Decimal(b), c) => {
1194 Some(Self::Decimal(decimal(a.decimal()?, b, c.decimal()?)))
1195 }
1196 (a, b, Self::Decimal(c)) => {
1197 Some(Self::Decimal(decimal(a.decimal()?, b.decimal()?, c)))
1198 }
1199 (a, b, c) => Some(Self::Float(float(a.float()?, b.float()?, c.float()?))),
1200 }
1201 }
1202}
1203
1204cast! {
1205 DecNum,
1206 self => match self {
1207 Self::Int(v) => v.into_value(),
1208 Self::Float(v) => v.into_value(),
1209 Self::Decimal(v) => v.into_value(),
1210 },
1211 v: i64 => Self::Int(v),
1212 v: f64 => Self::Float(v),
1213 v: Decimal => Self::Decimal(v),
1214}
1215
1216pub enum AngleLike {
1218 Int(i64),
1219 Float(f64),
1220 Angle(Angle),
1221}
1222
1223cast! {
1224 AngleLike,
1225 v: i64 => Self::Int(v),
1226 v: f64 => Self::Float(v),
1227 v: Angle => Self::Angle(v),
1228}
1229
1230#[cold]
1232fn too_large() -> &'static str {
1233 "the result is too large"
1234}
1235
1236#[cold]
1239fn cant_apply_to_decimal_and_float() -> HintedString {
1240 HintedString::new("cannot apply this operation to a decimal and a float".into())
1241 .with_hint(
1242 "if loss of precision is acceptable, explicitly cast the \
1243 decimal to a float with `float(value)`",
1244 )
1245}