概述
在串口通信、蓝牙通信中,经常需要将浮点数转换为字节发送,接收后再还原。App Inventor没有内置浮点数-字节转换,需要手动实现。
已有文档:hex_binary.md 介绍了进制转换和位运算。本文聚焦浮点数与字节的转换。
IEEE 754浮点数格式
单精度(32位/4字节)
| 部分 | 位数 | 说明 |
|---|---|---|
| 符号位 | 1位 | 0=正,1=负 |
| 指数位 | 8位 | 偏移127 |
| 尾数位 | 23位 | 小数部分 |
转换示例
浮点数 3.14 的IEEE 754表示:
二进制: 0 10000000 10010001111010111000011
十六进制: 40 48 F5 C3
字节序(大端): [0x40, 0x48, 0xF5, 0xC3]
方案一:通过WebView用JavaScript转换(推荐)
JavaScript的 DataView 提供精确的浮点数-字节转换:
HTML页面
<!-- float_converter.html -->
<script>
setInterval(function() {
var msg = window.AppInventor.getWebViewString();
if (msg && msg !== "") {
try {
var data = JSON.parse(msg);
if (data.cmd === "float2bytes") {
var buf = new ArrayBuffer(4);
var view = new DataView(buf);
view.setFloat32(0, data.value, data.littleEndian || false);
var bytes = [];
for (var i = 0; i < 4; i++) bytes.push(view.getUint8(i));
window.AppInventor.setWebViewString(JSON.stringify({result: bytes}));
} else if (data.cmd === "bytes2float") {
var buf = new ArrayBuffer(4);
var view = new DataView(buf);
for (var i = 0; i < data.bytes.length; i++) view.setUint8(i, data.bytes[i]);
var value = view.getFloat32(0, data.littleEndian || false);
window.AppInventor.setWebViewString(JSON.stringify({result: value}));
} else if (data.cmd === "double2bytes") {
var buf = new ArrayBuffer(8);
var view = new DataView(buf);
view.setFloat64(0, data.value, data.littleEndian || false);
var bytes = [];
for (var i = 0; i < 8; i++) bytes.push(view.getUint8(i));
window.AppInventor.setWebViewString(JSON.stringify({result: bytes}));
}
} catch(e) {
window.AppInventor.setWebViewString(JSON.stringify({error: e.message}));
}
}
}, 50);
</script>
App Inventor积木块
当 Screen1.初始化
设 WebView_转换器.主页地址 = "file:///android_asset/float_converter.html"
' 浮点数转字节
定义 浮点数转字节(数值, 小端序) 返回 字节列表
设 JSON = "{""cmd"":""float2bytes"",""value"":" & 数值 & ",""littleEndian"":" & 小端序 & "}"
设 WebView_转换器.WebView字符串 = JSON
' 等待WebView回调
当 WebView_转换器.WebView字符串改变(值)
设 结果 = 调用 文本.从JSON解析(值)
如果 从字典 结果 获取 "result" ≠ 未定义
设 字节列表 = 从字典 结果 获取 "result"
标签_字节.文本 = 列表转文本(字节列表, ", ")
' 字节转浮点数
定义 字节转浮点数(字节列表, 小端序) 返回 数值
设 JSON = "{""cmd"":""bytes2float"",""bytes"":[" & 列表转文本(字节列表, ",") & "],""littleEndian"":" & 小端序 & "}"
设 WebView_转换器.WebView字符串 = JSON
方案二:纯积木块实现(近似)
对于精度要求不高的场景,可以用数学方法拆分:
定义 浮点数转近似字节(数值) 返回 字节列表
' 简化版:将浮点数拆为整数部分和小数部分
' 注意:这不是标准IEEE 754,是自定义简化协议
设 整数部分 = 数学.向下取整(数学.绝对值(数值))
设 小数部分 = 数值 - 整数部分
' 整数部分用2字节
设 高字节 = 数学.向下取整(整数部分 / 256)
设 低字节 = 整数部分 模 256
' 小数部分用2字节(乘以10000保留4位精度)
设 小数整数 = 数学.向下取整(小数部分 * 10000)
设 小数高字节 = 数学.向下取整(小数整数 / 256)
设 小数低字节 = 小数整数 模 256
' 符号字节
设 符号 = 如果 数值 < 0 则 1 否则 0
返回 [符号, 高字节, 低字节, 小数高字节, 小数低字节]
定义 近似字节转浮点数(字节列表) 返回 数值
设 符号 = 列表第1项(字节列表)
设 整数部分 = 列表第2项(字节列表) * 256 + 列表第3项(字节列表)
设 小数部分 = (列表第4项(字节列表) * 256 + 列表第5项(字节列表)) / 10000
设 结果 = 整数部分 + 小数部分
如果 符号 = 1
设 结果 = -结果
返回 结果
方案三:通过蓝牙/串口直接发送
大多数嵌入式系统使用简单的自定义协议而非IEEE 754:
发送温度值(2字节整数 × 10)
定义 发送温度(温度值)
' 温度值 × 10 保留1位小数,用2字节整数发送
设 值 = 数学.向下取整(温度值 * 10)
设 高字节 = 数学.向下取整(值 / 256)
设 低字节 = 值 模 256
设 发送数据 = 字符(高字节) & 字符(低字节)
调用 蓝牙1.发送文本(发送数据)
' 接收端还原
定义 还原温度(高字节, 低字节) 返回 温度
设 值 = 高字节 * 256 + 低字节
返回 值 / 10.0
字节序(大小端)
| 字节序 | 说明 | 常见场景 |
|---|---|---|
| 大端序(Big-Endian) | 高位字节在前 | 网络协议、Java |
| 小端序(Little-Endian) | 低位字节在前 | x86、ARM、大多数MCU |
' 大端序: 3.14 → [0x40, 0x48, 0xF5, 0xC3]
' 小端序: 3.14 → [0xC3, 0xF5, 0x48, 0x40]
常见问题
Q1: 转换后数值有误差?
IEEE 754单精度只有约7位有效数字。3.14 可能变成 3.140000104904175。这是正常的浮点数精度问题。
Q2: 发送后MCU解析错误?
- 确认两端使用相同的字节序
- 确认数据类型一致(float/double)
- 使用hexdump工具查看原始字节确认
Q3: 有没有现成扩展?
部分串口扩展内置了浮点数转换功能。如果使用纯BluetoothClient,需要自行实现。
总结
| 方案 | 精度 | 难度 | 推荐度 |
|---|---|---|---|
| WebView + JS | ✅ IEEE 754 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 简化协议 | 近似 | ⭐ | ⭐⭐⭐ |
| 整数缩放 | 完全精确 | ⭐ | ⭐⭐⭐⭐ |
版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。
扫码添加客服咨询