-
Notifications
You must be signed in to change notification settings - Fork 0
Add FLOAT_NEGATE function
#11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1332efd
3361bae
69cc5a0
bf54998
4edd2fc
ee6cc3d
190c059
f5c62e7
fb838e9
3a4eec6
48b803a
f1834d4
b5b88f3
7d63f57
e695e7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,137 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| use super::*; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Helper to negate a Rain Float hex string while keeping full precision by | ||||||||||||||||||||||||||||||||||||||||||||||
| // operating on the binary representation directly. | ||||||||||||||||||||||||||||||||||||||||||||||
| fn float_negate_hex_to_hex(input_hex: &str) -> Result<String, String> { | ||||||||||||||||||||||||||||||||||||||||||||||
| let trimmed = input_hex.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if trimmed.is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||
| return Err("Empty string is not a valid hex number".to_string()); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Parse the input hex into a Float | ||||||||||||||||||||||||||||||||||||||||||||||
| let float_val = | ||||||||||||||||||||||||||||||||||||||||||||||
| Float::from_hex(trimmed).map_err(|e| format!("Failed to parse Float hex: {e}"))?; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Negate the float directly to avoid any formatting or precision loss. | ||||||||||||||||||||||||||||||||||||||||||||||
| let neg_float = (-float_val).map_err(|e| format!("Failed to negate Float value: {e}"))?; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Return as hex string | ||||||||||||||||||||||||||||||||||||||||||||||
| Ok(neg_float.as_hex()) | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // SQLite scalar function wrapper: FLOAT_NEGATE(hex_text) | ||||||||||||||||||||||||||||||||||||||||||||||
| pub unsafe extern "C" fn float_negate( | ||||||||||||||||||||||||||||||||||||||||||||||
| context: *mut sqlite3_context, | ||||||||||||||||||||||||||||||||||||||||||||||
| argc: c_int, | ||||||||||||||||||||||||||||||||||||||||||||||
| argv: *mut *mut sqlite3_value, | ||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if argc != 1 { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_error( | ||||||||||||||||||||||||||||||||||||||||||||||
| context, | ||||||||||||||||||||||||||||||||||||||||||||||
| c"FLOAT_NEGATE() requires exactly 1 argument".as_ptr(), | ||||||||||||||||||||||||||||||||||||||||||||||
| -1, | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
0xgleb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Return early for NULL inputs using the documented type check. | ||||||||||||||||||||||||||||||||||||||||||||||
| if sqlite3_value_type(*argv) == SQLITE_NULL { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_null(context); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Get the text value (now known to be non-NULL). | ||||||||||||||||||||||||||||||||||||||||||||||
| let value_ptr = sqlite3_value_text(*argv); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| let value_cstr = CStr::from_ptr(value_ptr as *const c_char); | ||||||||||||||||||||||||||||||||||||||||||||||
| let value_str = match value_cstr.to_str() { | ||||||||||||||||||||||||||||||||||||||||||||||
| Ok(value_str) => value_str, | ||||||||||||||||||||||||||||||||||||||||||||||
| Err(_) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_error(context, c"invalid UTF-8".as_ptr(), -1); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| match float_negate_hex_to_hex(value_str) { | ||||||||||||||||||||||||||||||||||||||||||||||
| Ok(result_hex) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(result_cstr) = CString::new(result_hex) { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_text( | ||||||||||||||||||||||||||||||||||||||||||||||
| context, | ||||||||||||||||||||||||||||||||||||||||||||||
| result_cstr.as_ptr(), | ||||||||||||||||||||||||||||||||||||||||||||||
| result_cstr.as_bytes().len() as c_int, | ||||||||||||||||||||||||||||||||||||||||||||||
| SQLITE_TRANSIENT(), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_error(context, c"Failed to create result string".as_ptr(), -1); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Err(e) => match CString::new(e) { | ||||||||||||||||||||||||||||||||||||||||||||||
| Ok(error_msg) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_error(context, error_msg.as_ptr(), -1); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Err(_) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| sqlite3_result_error( | ||||||||||||||||||||||||||||||||||||||||||||||
| context, | ||||||||||||||||||||||||||||||||||||||||||||||
| c"Error message contained interior NUL".as_ptr(), | ||||||||||||||||||||||||||||||||||||||||||||||
| -1, | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||
| mod tests { | ||||||||||||||||||||||||||||||||||||||||||||||
| use super::*; | ||||||||||||||||||||||||||||||||||||||||||||||
| use wasm_bindgen_test::*; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[wasm_bindgen_test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn test_float_negate_hex_to_hex_pos_to_neg() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let pos_hex = Float::parse("1.5".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let expected_neg_hex = Float::parse("-1.5".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let out = float_negate_hex_to_hex(&pos_hex).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(out, expected_neg_hex); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[wasm_bindgen_test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn test_float_negate_hex_to_hex_neg_to_pos() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let neg_hex = Float::parse("-2.25".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let expected_pos_hex = Float::parse("2.25".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let out = float_negate_hex_to_hex(&neg_hex).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(out, expected_pos_hex); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[wasm_bindgen_test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn test_float_negate_hex_to_hex_zero() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let zero_hex = Float::parse("0".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let expected_zero_hex = Float::parse("0".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let out = float_negate_hex_to_hex(&zero_hex).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(out, expected_zero_hex); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+105
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick Add a test for signed zero behavior. If Float distinguishes -0 and 0, assert expected normalization. fn test_float_negate_hex_to_hex_zero() {
let zero_hex = Float::parse("0".to_string()).unwrap().as_hex();
let expected_zero_hex = Float::parse("0".to_string()).unwrap().as_hex();
let out = float_negate_hex_to_hex(&zero_hex).unwrap();
assert_eq!(out, expected_zero_hex);
}
+ #[wasm_bindgen_test]
+ fn test_float_negate_hex_to_hex_negative_zero() {
+ let neg_zero_hex = Float::parse("-0".to_string()).unwrap().as_hex();
+ let out = float_negate_hex_to_hex(&neg_zero_hex).unwrap();
+ // Expect normalization to +0; adjust if library preserves -0
+ let zero_hex = Float::parse("0".to_string()).unwrap().as_hex();
+ assert_eq!(out, zero_hex);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to test this out. out of scope There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @findolor Understood! No problem at all - I'll skip this test suggestion since it's out of scope for this PR. The current test coverage looks good for the main functionality. 🧠 Learnings usedThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Float doesn't distinguish between -0 and 0 as far as I remember There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[wasm_bindgen_test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn test_float_negate_hex_to_hex_high_precision() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let input = "300.123456789012345678"; | ||||||||||||||||||||||||||||||||||||||||||||||
| let in_hex = Float::parse(input.to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let expected_hex = Float::parse(format!("-{input}")).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let out = float_negate_hex_to_hex(&in_hex).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(out, expected_hex); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[wasm_bindgen_test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn test_float_negate_hex_to_hex_whitespace() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let in_hex = Float::parse("10".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let wrapped = format!(" {in_hex} "); | ||||||||||||||||||||||||||||||||||||||||||||||
| let expected_hex = Float::parse("-10".to_string()).unwrap().as_hex(); | ||||||||||||||||||||||||||||||||||||||||||||||
| let out = float_negate_hex_to_hex(&wrapped).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(out, expected_hex); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[wasm_bindgen_test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn test_float_negate_hex_to_hex_invalid() { | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(float_negate_hex_to_hex("0XBADHEX").is_err()); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(float_negate_hex_to_hex("").is_err()); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(float_negate_hex_to_hex("not_hex").is_err()); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.