mz_expr/scalar/func/impls/
float64.rs1use chrono::{DateTime, Utc};
11use mz_expr_derive::sqlfunc;
12use mz_ore::cast::TryCastFrom;
13use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
14use mz_repr::adt::timestamp::CheckedTimestamp;
15use mz_repr::{SqlScalarType, strconv};
16use serde::{Deserialize, Serialize};
17
18use crate::EvalError;
19use crate::scalar::DomainLimit;
20
21#[sqlfunc(
22 sqlname = "-",
23 preserves_uniqueness = false,
24 inverse = NegFloat64,
25 is_monotone = true
26)]
27fn neg_float64(a: f64) -> f64 {
28 -a
29}
30
31#[sqlfunc(sqlname = "abs")]
32fn abs_float64(a: f64) -> f64 {
33 a.abs()
34}
35
36#[sqlfunc(sqlname = "roundf64")]
37fn round_float64(a: f64) -> f64 {
38 a.round_ties_even()
39}
40
41#[sqlfunc(sqlname = "truncf64")]
42fn trunc_float64(a: f64) -> f64 {
43 a.trunc()
44}
45
46#[sqlfunc(sqlname = "ceilf64")]
47fn ceil_float64(a: f64) -> f64 {
48 a.ceil()
49}
50
51#[sqlfunc(sqlname = "floorf64")]
52fn floor_float64(a: f64) -> f64 {
53 a.floor()
54}
55
56#[sqlfunc(
57 sqlname = "double_to_smallint",
58 preserves_uniqueness = false,
59 inverse = super::CastInt16ToFloat64,
60 is_monotone = true
61)]
62fn cast_float64_to_int16(a: f64) -> Result<i16, EvalError> {
63 let f = round_float64(a);
64 #[allow(clippy::as_conversions)]
66 if (f >= (i16::MIN as f64)) && (f < -(i16::MIN as f64)) {
67 Ok(f as i16)
68 } else {
69 Err(EvalError::Int16OutOfRange(f.to_string().into()))
70 }
71}
72
73#[sqlfunc(
74 sqlname = "double_to_integer",
75 preserves_uniqueness = false,
76 inverse = super::CastInt32ToFloat64,
77 is_monotone = true
78)]
79fn cast_float64_to_int32(a: f64) -> Result<i32, EvalError> {
80 let f = round_float64(a);
81 #[allow(clippy::as_conversions)]
87 if (f >= (i32::MIN as f64)) && (f < -(i32::MIN as f64)) {
88 Ok(f as i32)
89 } else {
90 Err(EvalError::Int32OutOfRange(f.to_string().into()))
91 }
92}
93
94#[sqlfunc(
95 sqlname = "f64toi64",
96 preserves_uniqueness = false,
97 inverse = super::CastInt64ToFloat64,
98 is_monotone = true
99)]
100fn cast_float64_to_int64(a: f64) -> Result<i64, EvalError> {
101 let f = round_float64(a);
102 #[allow(clippy::as_conversions)]
108 if (f >= (i64::MIN as f64)) && (f < -(i64::MIN as f64)) {
109 Ok(f as i64)
110 } else {
111 Err(EvalError::Int64OutOfRange(f.to_string().into()))
112 }
113}
114
115#[sqlfunc(
116 sqlname = "double_to_real",
117 preserves_uniqueness = false,
118 inverse = super::CastFloat32ToFloat64,
119 is_monotone = true
120)]
121fn cast_float64_to_float32(a: f64) -> Result<f32, EvalError> {
122 #[allow(clippy::as_conversions)]
124 let result = a as f32;
125 if result.is_infinite() && !a.is_infinite() {
126 Err(EvalError::FloatOverflow)
127 } else if result == 0.0 && a != 0.0 {
128 Err(EvalError::FloatUnderflow)
129 } else {
130 Ok(result)
131 }
132}
133
134#[sqlfunc(
135 sqlname = "double_to_text",
136 preserves_uniqueness = false,
137 inverse = super::CastStringToFloat64
138)]
139fn cast_float64_to_string(a: f64) -> String {
140 let mut s = String::new();
141 strconv::format_float64(&mut s, a);
142 s
143}
144
145#[sqlfunc(
146 sqlname = "double_to_uint2",
147 preserves_uniqueness = false,
148 inverse = super::CastUint16ToFloat64,
149 is_monotone = true
150)]
151fn cast_float64_to_uint16(a: f64) -> Result<u16, EvalError> {
152 let f = round_float64(a);
153 #[allow(clippy::as_conversions)]
155 if (f >= 0.0) && (f <= (u16::MAX as f64)) {
156 Ok(f as u16)
157 } else {
158 Err(EvalError::UInt16OutOfRange(f.to_string().into()))
159 }
160}
161
162#[sqlfunc(
163 sqlname = "double_to_uint4",
164 preserves_uniqueness = false,
165 inverse = super::CastUint32ToFloat64,
166 is_monotone = true
167)]
168fn cast_float64_to_uint32(a: f64) -> Result<u32, EvalError> {
169 let f = round_float64(a);
170 #[allow(clippy::as_conversions)]
172 if (f >= 0.0) && (f <= (u32::MAX as f64)) {
173 Ok(f as u32)
174 } else {
175 Err(EvalError::UInt32OutOfRange(f.to_string().into()))
176 }
177}
178
179#[sqlfunc(
180 sqlname = "double_to_uint8",
181 preserves_uniqueness = false,
182 inverse = super::CastUint64ToFloat64,
183 is_monotone = true
184)]
185fn cast_float64_to_uint64(a: f64) -> Result<u64, EvalError> {
186 let f = round_float64(a);
187 #[allow(clippy::as_conversions)]
193 if (f >= 0.0) && (f < (u64::MAX as f64)) {
194 Ok(f as u64)
195 } else {
196 Err(EvalError::UInt64OutOfRange(f.to_string().into()))
197 }
198}
199
200#[derive(
201 Ord,
202 PartialOrd,
203 Clone,
204 Debug,
205 Eq,
206 PartialEq,
207 Serialize,
208 Deserialize,
209 Hash
210)]
211pub struct CastFloat64ToNumeric(pub Option<NumericMaxScale>);
212
213#[sqlfunc(
214 CastFloat64ToNumeric,
215 sqlname = "double_to_numeric",
216 inverse = super::CastNumericToFloat64,
217 is_monotone = true,
218 output_type_expr = SqlScalarType::Numeric { max_scale: self.0 }
219 .nullable(input_type.nullable)
220)]
221fn cast_float64_to_numeric(&self, a: f64) -> Result<Numeric, EvalError> {
222 if a.is_infinite() {
223 return Err(EvalError::InfinityOutOfDomain(
224 "casting double precision to numeric".into(),
225 ));
226 }
227 let mut a = Numeric::from(a);
228 if let Some(scale) = self.0 {
229 if numeric::rescale(&mut a, scale.into_u8()).is_err() {
230 return Err(EvalError::NumericFieldOverflow);
231 }
232 }
233 match numeric::munge_numeric(&mut a) {
234 Ok(_) => Ok(a),
235 Err(_) => Err(EvalError::NumericFieldOverflow),
236 }
237}
238
239#[sqlfunc(sqlname = "sqrtf64")]
240fn sqrt_float64(a: f64) -> Result<f64, EvalError> {
241 if a < 0.0 {
242 return Err(EvalError::NegSqrt);
243 }
244 Ok(a.sqrt())
245}
246
247#[sqlfunc(sqlname = "cbrtf64")]
248fn cbrt_float64(a: f64) -> f64 {
249 a.cbrt()
250}
251
252#[sqlfunc]
253fn cos(a: f64) -> Result<f64, EvalError> {
254 if a.is_infinite() {
255 return Err(EvalError::InfinityOutOfDomain("cos".into()));
256 }
257 Ok(a.cos())
258}
259
260#[sqlfunc]
261fn acos(a: f64) -> Result<f64, EvalError> {
262 if a < -1.0 || 1.0 < a {
263 return Err(EvalError::OutOfDomain(
264 DomainLimit::Inclusive(-1),
265 DomainLimit::Inclusive(1),
266 "acos".into(),
267 ));
268 }
269 Ok(a.acos())
270}
271
272#[sqlfunc]
273fn cosh(a: f64) -> f64 {
274 a.cosh()
275}
276
277#[sqlfunc]
278fn acosh(a: f64) -> Result<f64, EvalError> {
279 if a < 1.0 {
280 return Err(EvalError::OutOfDomain(
281 DomainLimit::Inclusive(1),
282 DomainLimit::None,
283 "acosh".into(),
284 ));
285 }
286 Ok(a.acosh())
287}
288
289#[sqlfunc]
290fn sin(a: f64) -> Result<f64, EvalError> {
291 if a.is_infinite() {
292 return Err(EvalError::InfinityOutOfDomain("sin".into()));
293 }
294 Ok(a.sin())
295}
296
297#[sqlfunc]
298fn asin(a: f64) -> Result<f64, EvalError> {
299 if a < -1.0 || 1.0 < a {
300 return Err(EvalError::OutOfDomain(
301 DomainLimit::Inclusive(-1),
302 DomainLimit::Inclusive(1),
303 "asin".into(),
304 ));
305 }
306 Ok(a.asin())
307}
308
309#[sqlfunc]
310fn sinh(a: f64) -> f64 {
311 a.sinh()
312}
313
314#[sqlfunc]
315fn asinh(a: f64) -> f64 {
316 a.asinh()
317}
318
319#[sqlfunc]
320fn tan(a: f64) -> Result<f64, EvalError> {
321 if a.is_infinite() {
322 return Err(EvalError::InfinityOutOfDomain("tan".into()));
323 }
324 Ok(a.tan())
325}
326
327#[sqlfunc]
328fn atan(a: f64) -> f64 {
329 a.atan()
330}
331
332#[sqlfunc]
333fn tanh(a: f64) -> f64 {
334 a.tanh()
335}
336
337#[sqlfunc]
338fn atanh(a: f64) -> Result<f64, EvalError> {
339 if a < -1.0 || 1.0 < a {
340 return Err(EvalError::OutOfDomain(
341 DomainLimit::Inclusive(-1),
342 DomainLimit::Inclusive(1),
343 "atanh".into(),
344 ));
345 }
346 Ok(a.atanh())
347}
348
349#[sqlfunc]
350fn cot(a: f64) -> Result<f64, EvalError> {
351 if a.is_infinite() {
352 return Err(EvalError::InfinityOutOfDomain("cot".into()));
353 }
354 Ok(1.0 / a.tan())
355}
356
357#[sqlfunc]
358fn radians(a: f64) -> f64 {
359 a.to_radians()
360}
361
362#[sqlfunc]
363fn degrees(a: f64) -> f64 {
364 a.to_degrees()
365}
366
367#[sqlfunc(sqlname = "log10f64")]
368fn log10(a: f64) -> Result<f64, EvalError> {
369 if a.is_sign_negative() {
370 return Err(EvalError::NegativeOutOfDomain("log10".into()));
371 }
372 if a == 0.0 {
373 return Err(EvalError::ZeroOutOfDomain("log10".into()));
374 }
375 Ok(a.log10())
376}
377
378#[sqlfunc(sqlname = "lnf64")]
379fn ln(a: f64) -> Result<f64, EvalError> {
380 if a.is_sign_negative() {
381 return Err(EvalError::NegativeOutOfDomain("ln".into()));
382 }
383 if a == 0.0 {
384 return Err(EvalError::ZeroOutOfDomain("ln".into()));
385 }
386 Ok(a.ln())
387}
388
389#[sqlfunc(sqlname = "expf64")]
390fn exp(a: f64) -> Result<f64, EvalError> {
391 let r = a.exp();
392 if r.is_infinite() {
393 return Err(EvalError::FloatOverflow);
394 }
395 if r == 0.0 {
396 return Err(EvalError::FloatUnderflow);
397 }
398 Ok(r)
399}
400
401#[sqlfunc(sqlname = "mz_sleep")]
402fn sleep(a: f64) -> Result<Option<CheckedTimestamp<DateTime<Utc>>>, EvalError> {
403 let duration = std::time::Duration::try_from_secs_f64(a).map_err(|_| {
404 let mut val = String::new();
405 strconv::format_float64(&mut val, a);
406 EvalError::InvalidParameterValue(format!("cannot sleep for {val} seconds").into())
407 })?;
408 std::thread::sleep(duration);
409 Ok(None)
410}
411
412#[sqlfunc(sqlname = "tots")]
413fn to_timestamp(f: f64) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
414 const NANO_SECONDS_PER_SECOND: i64 = 1_000_000_000;
415 if f.is_nan() {
416 Err(EvalError::TimestampCannotBeNan)
417 } else if f.is_infinite() {
418 Err(EvalError::TimestampOutOfRange)
420 } else {
421 let mut secs = i64::try_cast_from(f.trunc()).ok_or(EvalError::TimestampOutOfRange)?;
422 let microsecs = (f.fract() * 1_000_000.0).round();
428 let mut nanosecs =
429 i64::try_cast_from(microsecs * 1_000.0).ok_or(EvalError::TimestampOutOfRange)?;
430 if nanosecs < 0 {
431 secs = secs.checked_sub(1).ok_or(EvalError::TimestampOutOfRange)?;
432 nanosecs = NANO_SECONDS_PER_SECOND
433 .checked_add(nanosecs)
434 .ok_or(EvalError::TimestampOutOfRange)?;
435 }
436 secs = secs
438 .checked_add(nanosecs / NANO_SECONDS_PER_SECOND)
439 .ok_or(EvalError::TimestampOutOfRange)?;
440 nanosecs %= NANO_SECONDS_PER_SECOND;
441 let nanosecs = u32::try_from(nanosecs).map_err(|_| EvalError::TimestampOutOfRange)?;
442 match DateTime::from_timestamp(secs, nanosecs) {
443 Some(dt) => {
444 CheckedTimestamp::from_timestamplike(dt).map_err(|_| EvalError::TimestampOutOfRange)
445 }
446 None => Err(EvalError::TimestampOutOfRange),
447 }
448 }
449}