mz_expr/scalar/func/impls/
float32.rs1use std::fmt;
11
12use mz_expr_derive::sqlfunc;
13use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
14use mz_repr::{SqlColumnType, SqlScalarType, strconv};
15use serde::{Deserialize, Serialize};
16
17use crate::EvalError;
18use crate::scalar::func::EagerUnaryFunc;
19
20#[sqlfunc(
21 sqlname = "-",
22 preserves_uniqueness = false,
23 inverse = to_unary!(NegFloat32),
24 is_monotone = true
25)]
26fn neg_float32(a: f32) -> f32 {
27 -a
28}
29
30#[sqlfunc(sqlname = "abs")]
31fn abs_float32(a: f32) -> f32 {
32 a.abs()
33}
34
35#[sqlfunc(sqlname = "roundf32")]
36fn round_float32(a: f32) -> f32 {
37 a.round_ties_even()
38}
39
40#[sqlfunc(sqlname = "truncf32")]
41fn trunc_float32(a: f32) -> f32 {
42 a.trunc()
43}
44
45#[sqlfunc(sqlname = "ceilf32")]
46fn ceil_float32(a: f32) -> f32 {
47 a.ceil()
48}
49
50#[sqlfunc(sqlname = "floorf32")]
51fn floor_float32(a: f32) -> f32 {
52 a.floor()
53}
54
55#[sqlfunc(
56 sqlname = "real_to_smallint",
57 preserves_uniqueness = false,
58 inverse = to_unary!(super::CastInt16ToFloat32),
59 is_monotone = true
60)]
61fn cast_float32_to_int16(a: f32) -> Result<i16, EvalError> {
62 let f = round_float32(a);
63 #[allow(clippy::as_conversions)]
65 if (f >= (i16::MIN as f32)) && (f < -(i16::MIN as f32)) {
66 Ok(f as i16)
67 } else {
68 Err(EvalError::Int16OutOfRange(f.to_string().into()))
69 }
70}
71
72#[sqlfunc(
73 sqlname = "real_to_integer",
74 preserves_uniqueness = false,
75 inverse = to_unary!(super::CastInt32ToFloat32),
76 is_monotone = true
77)]
78fn cast_float32_to_int32(a: f32) -> Result<i32, EvalError> {
79 let f = round_float32(a);
80 #[allow(clippy::as_conversions)]
86 if (f >= (i32::MIN as f32)) && (f < -(i32::MIN as f32)) {
87 Ok(f as i32)
88 } else {
89 Err(EvalError::Int32OutOfRange(f.to_string().into()))
90 }
91}
92
93#[sqlfunc(
94 sqlname = "real_to_bigint",
95 preserves_uniqueness = false,
96 inverse = to_unary!(super::CastInt64ToFloat32),
97 is_monotone = true
98)]
99fn cast_float32_to_int64(a: f32) -> Result<i64, EvalError> {
100 let f = round_float32(a);
101 #[allow(clippy::as_conversions)]
107 if (f >= (i64::MIN as f32)) && (f < -(i64::MIN as f32)) {
108 Ok(f as i64)
109 } else {
110 Err(EvalError::Int64OutOfRange(f.to_string().into()))
111 }
112}
113
114#[sqlfunc(
115 sqlname = "real_to_double",
116 preserves_uniqueness = false,
117 inverse = to_unary!(super::CastFloat64ToFloat32),
118 is_monotone = true
119)]
120fn cast_float32_to_float64(a: f32) -> f64 {
121 a.into()
122}
123
124#[sqlfunc(
125 sqlname = "real_to_text",
126 preserves_uniqueness = false,
127 inverse = to_unary!(super::CastStringToFloat32)
128)]
129fn cast_float32_to_string(a: f32) -> String {
130 let mut s = String::new();
131 strconv::format_float32(&mut s, a);
132 s
133}
134
135#[sqlfunc(
136 sqlname = "real_to_uint2",
137 preserves_uniqueness = false,
138 inverse = to_unary!(super::CastUint16ToFloat32),
139 is_monotone = true
140)]
141fn cast_float32_to_uint16(a: f32) -> Result<u16, EvalError> {
142 let f = round_float32(a);
143 #[allow(clippy::as_conversions)]
145 if (f >= 0.0) && (f <= (u16::MAX as f32)) {
146 Ok(f as u16)
147 } else {
148 Err(EvalError::UInt16OutOfRange(f.to_string().into()))
149 }
150}
151
152#[sqlfunc(
153 sqlname = "real_to_uint4",
154 preserves_uniqueness = false,
155 inverse = to_unary!(super::CastUint32ToFloat32),
156 is_monotone = true
157)]
158fn cast_float32_to_uint32(a: f32) -> Result<u32, EvalError> {
159 let f = round_float32(a);
160 #[allow(clippy::as_conversions)]
165 if (f >= 0.0) && (f < (u32::MAX as f32)) {
166 Ok(f as u32)
167 } else {
168 Err(EvalError::UInt32OutOfRange(f.to_string().into()))
169 }
170}
171
172#[sqlfunc(
173 sqlname = "real_to_uint8",
174 preserves_uniqueness = false,
175 inverse = to_unary!(super::CastUint64ToFloat32),
176 is_monotone = true
177)]
178fn cast_float32_to_uint64(a: f32) -> Result<u64, EvalError> {
179 let f = round_float32(a);
180 #[allow(clippy::as_conversions)]
185 if (f >= 0.0) && (f < (u64::MAX as f32)) {
186 Ok(f as u64)
187 } else {
188 Err(EvalError::UInt64OutOfRange(f.to_string().into()))
189 }
190}
191
192#[derive(
193 Ord,
194 PartialOrd,
195 Clone,
196 Debug,
197 Eq,
198 PartialEq,
199 Serialize,
200 Deserialize,
201 Hash
202)]
203pub struct CastFloat32ToNumeric(pub Option<NumericMaxScale>);
204
205impl EagerUnaryFunc for CastFloat32ToNumeric {
206 type Input<'a> = f32;
207 type Output<'a> = Result<Numeric, EvalError>;
208
209 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
210 if a.is_infinite() {
211 return Err(EvalError::InfinityOutOfDomain(
212 "casting real to numeric".into(),
213 ));
214 }
215 let mut a = Numeric::from(a);
216 if let Some(scale) = self.0 {
217 if numeric::rescale(&mut a, scale.into_u8()).is_err() {
218 return Err(EvalError::NumericFieldOverflow);
219 }
220 }
221 numeric::munge_numeric(&mut a).unwrap();
222 Ok(a)
223 }
224
225 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
226 SqlScalarType::Numeric { max_scale: self.0 }.nullable(input.nullable)
227 }
228
229 fn inverse(&self) -> Option<crate::UnaryFunc> {
230 to_unary!(super::CastNumericToFloat32)
231 }
232
233 fn is_monotone(&self) -> bool {
234 true
235 }
236}
237
238impl fmt::Display for CastFloat32ToNumeric {
239 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240 f.write_str("real_to_numeric")
241 }
242}