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