我是Ada的新开发人员,如果我不够清楚,请原谅我。 我面临一个问题,我不知道从哪里来过错。我首先解释一下上下文: 我拥有一套针对qemu(BE)的测试。我希望使用pragma Default_Scalar_Storage_Order(High_Order_First)在PC本机(x86)上执行它们。我注意到我的某些测试效果很好,但包括float在内的测试却并非如此。为简单起见,我编写了一个包含FLOAT和INT的测试。
with AUNIT.ASSERTIONS; use AUNIT.ASSERTIONS;
with BASIC_TYPES;
with BASIC_TYPES.STREAM;
with INTERFACES;
with ADA.INTEGER_TEXT_IO;
with ADA.FLOAT_TEXT_IO;
with ADA.TEXT_IO;
with STREAMS;
with SYSTEM;
package body TEST.TEST is
function Integer2Hexa(Hex_Int : Integer; Bits_Nbr : Integer) return String is
Hexa : String(1..Bits_Nbr);
begin
Ada.Integer_Text_IO.Put(Hexa,Hex_Int,16);
return Hexa;
end Integer2Hexa;
function NAME (T : TEST) return AUNIT.MESSAGE_STRING is
pragma UNREFERENCED (T);
begin
return AUNIT.FORMAT ("Test package");
end NAME;
IntegerNbr : BASIC_TYPES.INT32_T;
FloatNbr : INTERFACES.IEEE_Float_32;
procedure RUN_TEST (T : in out TEST) is
PACKED_ARRAY : BASIC_TYPES.UINT8_ARRAY_NC_T (1 .. 8) := (others => 0);
MY_STREAM : STREAMS.STREAM_T;
use type BASIC_TYPES.UINT8_ARRAY_NC_T;
begin
IntegerNbr := 479037433;
FloatNbr := 2.0012151e+09;
ADA.TEXT_IO.PUT_LINE ("Default bit order: " & SYSTEM.Default_Bit_Order'IMG);
ADA.TEXT_IO.PUT_LINE ("Integer size : " & INTEGER'IMAGE (INTEGER'SIZE));
ADA.TEXT_IO.PUT ("16#4EEE903D#"); -- 2.0012151e+09 in FLOAT BIG ENDIAN
ADA.TEXT_IO.PUT (Integer2Hexa(Integer(IntegerNbr),32)); -- 16#1C8D87F9# in INT BIG ENDIAN
ADA.TEXT_IO.NEW_LINE;
-- Init the stream
STREAMS.INIT (MY_STREAM => MY_STREAM,
STREAM_ADDRESS => PACKED_ARRAY (PACKED_ARRAY'FIRST)'ADDRESS,
STREAM_SIZE => PACKED_ARRAY'LENGTH);
BASIC_TYPES.STREAM.WRITE_FLOAT_T (MY_STREAM => MY_STREAM,
ITEM => FloatNbr,
ALIGN_MODE => STREAMS.PACK);
BASIC_TYPES.STREAM.WRITE_INT32_T (MY_STREAM => MY_STREAM,
ITEM => IntegerNbr,
ALIGN_MODE => STREAMS.PACK);
if (not ASSERT(PACKED_ARRAY = (16#4e#, 16#ee#, 16#90#, 16#3d#, 16#1c#, 16#8d#, 16#87#, 16#f9#), "PACKED_ARRAY incorrect")) then
for I in PACKED_ARRAY'RANGE loop
ADA.TEXT_IO.PUT (Integer2Hexa(Integer(PACKED_ARRAY (I)),8));
end loop;
ADA.TEXT_IO.NEW_LINE;
end if;
end RUN_TEST;
end TEST.TEST;
我注意到INT的编写正确,但FLOAT并非如此(它是用Little Endian编写的)。确实在出口我应该有
16#4e#, 16#ee#, 16#90#, 16#3d#, 16#1c#, 16#8d#, 16#87#, 16#f9#
但我知道
16#3d#, 16#90#, 16#ee#, 16#4e#, 16#1c#, 16#8d#, 16#87#, 16#f9#
我使用此网站来确认结果:https://www.scadacore.com/tools/programming-calculators/online-hex-converter/
我不知道由于编译指示而导致的转换是否正确用于FLOAT。我使用PRAGMA.txt中的文本在软件包Compiler的gpr文件中调用它:pragma Default_Scalar_Storage_Order(High_Order_First);
package Compiler is
for Local_Configuration_Pragmas use "PRAGMAS.txt";
for Switches ("ada") use ("-g");
end Compiler;
问题是否出在我使用编译指示的方式上?
这是被调用的过程:
procedure WRITE_FLOAT_T
(MY_STREAM : in out STREAMS.STREAM_T;
ITEM : in BASIC_TYPES.FLOAT_T;
ALIGN_MODE : in STREAMS.ALIGN_MODE_T)
is
pragma UNREFERENCED (ALIGN_MODE);
-- Temporary types for non pack case
type TMP_TYPE_T is new STANDARD.FLOAT;
for TMP_TYPE_T'VALUE_SIZE use FLOAT_T_SIZE_C;
TMP_TYPE : TMP_TYPE_T;
subtype BITS_FIELD_T is STREAMS.BIT_FIELD_ARR_NC_T (1 .. STREAMS.SIZE_T (FLOAT_T_SIZE_C));
function TO_BITS_ARRAY is new UNCHECKED_CONVERSION (TMP_TYPE_T,
BITS_FIELD_T);
begin
-- Convert item to a temporary type
TMP_TYPE := TMP_TYPE_T(ITEM);
STREAMS.WRITE (MY_STREAM => MY_STREAM,
DATA => TO_BITS_ARRAY(TMP_TYPE));
end WRITE_FLOAT_T;
procedure WRITE (MY_STREAM : in out STREAM_T;
DATA : in BIT_FIELD_ARR_NC_T) is
begin
if (MY_STREAM.ERROR_CODE = NO_ERROR)
and then (MY_STREAM.WRITE_OFFSET + DATA'LENGTH - 1 <= MY_STREAM.STREAM_SIZE * 8) then
if (MY_STREAM.WRITE_OFFSET mod 8 = 1) and then (DATA'LENGTH mod 8 = 0) then
-- Byte mode
WRITE_BYTES(MY_STREAM => MY_STREAM,
DATA => DATA);
else
-- Bit mode
WRITE_BITS(MY_STREAM => MY_STREAM,
DATA => DATA);
end if;
elsif (MY_STREAM.ERROR_CODE = NO_ERROR) then
-- Update ERROR_CODE on first error
MY_STREAM.ERROR_CODE := END_ERROR;
end if;
end WRITE;
procedure WRITE_BYTES (MY_STREAM : in out STREAM_T;
DATA : in BIT_FIELD_ARR_NC_T) is
BYTE_FIELD_ARR : BYTE_FIELD_ARR_NC_T (1 .. MY_STREAM.STREAM_SIZE);
for BYTE_FIELD_ARR'ADDRESS use MY_STREAM.STREAM_ADDRESS;
TMP_BYTE_FIELD_ARR : BYTE_FIELD_ARR_NC_T (1 .. DATA'LENGTH / 8);
for TMP_BYTE_FIELD_ARR'ADDRESS use DATA'ADDRESS;
begin
-- Write byte field
BYTE_FIELD_ARR ((MY_STREAM.WRITE_OFFSET + 7) / 8 .. (MY_STREAM.WRITE_OFFSET + 7) / 8 + (DATA'LENGTH / 8) - 1) := TMP_BYTE_FIELD_ARR;
MY_STREAM.WRITE_OFFSET := MY_STREAM.WRITE_OFFSET + DATA'LENGTH;
end WRITE_BYTES;
提前谢谢!
Q.Dherb
答案 0 :(得分:1)
根据Scalar_Storage_Order的文档:
此实现定义的属性仅适用于数组和记录。这意味着它对标量类型(如Float或Integer)的内存布局没有影响。无论Default_Scalar_Storage_Order属性的值如何,在大字节序计算机上,16#12345678#整数将表示为12 34 56 78,在低字节序计算机上,它将表示为78 56 34 12。
对于数组,它确定每个标量分量的storage_element的顺序(通常是字节)。对于您而言,所有数组组件的大小均小于或等于存储元素,这意味着Scalar_Storage_Order子句无效。
以下是显示此子句对数组的影响的示例:
with Ada.Text_IO;
with System;
with Interfaces;
with Ada.Streams;
with Ada.Integer_Text_IO;
procedure Scalar_Storage_Element_Exemple is
type T_U16_Arr_Le is array (Positive range <>) of Interfaces.Unsigned_16
with Component_Size => 16, Scalar_Storage_Order => System.Low_Order_First;
type T_U16_Arr_Be is array (Positive range <>) of Interfaces.Unsigned_16
with Component_Size => 16, Scalar_Storage_Order => System.High_Order_First;
type T_U8_Arr_Le is array (Positive range <>) of Interfaces.Unsigned_8
with Component_Size => 8, Scalar_Storage_Order => System.Low_Order_First;
type T_U8_Arr_Be is array (Positive range <>) of Interfaces.Unsigned_8
with Component_Size => 8, Scalar_Storage_Order => System.High_Order_First;
Arr_16_LE : T_U16_Arr_Le (1 .. 2) := (16#1234#, 16#5678#);
Arr_16_BE : T_U16_Arr_Be (1 .. 2) := (16#1234#, 16#5678#);
Arr_8_LE : T_U8_Arr_Le (1 .. 4) := (16#12#, 16#34#, 16#56#, 16#78#);
Arr_8_BE : T_U8_Arr_Be (1 .. 4) := (16#12#, 16#34#, 16#56#, 16#78#);
Sea_16_LE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_16_LE'Address;
Sea_16_BE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_16_BE'Address;
Sea_8_LE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_8_LE'Address;
Sea_8_BE : Ada.Streams.Stream_Element_Array (1 .. 4) with Address => Arr_8_BE'Address;
function byte2Hexa(byte : Integer) return String is
Hexa : String(1..8);
begin
Ada.Integer_Text_IO.Put(Hexa,byte,16);
return Hexa;
end byte2Hexa;
begin
for byte of Sea_16_LE loop
Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
end loop;
-- display 16#34# 16#12# 16#78# 16#56#
-- each item of the array is in LE
Ada.Text_IO.New_Line;
for byte of Sea_16_BE loop
Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
end loop;
-- 16#12# 16#34# 16#56# 16#78#
-- each item of the array is in BE
Ada.Text_IO.New_Line;
for byte of Sea_8_LE loop
Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
end loop;
-- 16#12# 16#34# 16#56# 16#78#
-- no effect as size of component is inferior or equal to storage_element size
Ada.Text_IO.New_Line;
for byte of Sea_8_BE loop
Ada.Text_IO.Put(byte2Hexa(Integer(byte)));
end loop;
-- 16#12# 16#34# 16#56# 16#78#
-- no effect as size of component is inferior or equal to storage_element size
end Scalar_Storage_Element_Exemple;
您的float序列化可在您的QEMU上使用,因为您已经在使用BE。因此,Scalar_Storage_Order仅用于确认,没有作用。
它在x86上不起作用,因为本机字节序为LE,并且如前所述,BE Scalar_Storage_Order子句对所涉及的类型无效。因此最终结果是LE浮点数。
假设您使用相同的逻辑进行序列化(未提供相关代码,因此我认为它是不同的),那么Integer或Float在此处的行为应类似。
答案 1 :(得分:0)
这还不是很清楚,因为您已经包含了许多令人困惑的细节,但是我认为您正在尝试以与字节序无关的方式写入流,以便进行通信(通过网络?)在不同字节序的机器之间。
过程WRITE_FLOAT_T
的问题在于它的ITEM
是普通浮点数,因此Scalar_Storage_Order
没有任何作用。
The way I've used Scalar_Storage_Order
是要声明我要发送的记录,
type SNTP_Packet is record
-- contents
end record
with
Bit_Order => System.High_Order_First,
Scalar_Storage_Order => System.High_Order_First,
Size => 48 * 8;
for SNTP_Packet use record
-- placement of content
end record;
subtype Net_Packet is Ada.Streams.Stream_Element_Array (1 .. 48);
-- This is what actually gets streamed
function To_Net_Packet
is new Ada.Unchecked_Conversion (SNTP_Packet, Net_Packet);
function To_SNTP_Packet
is new Ada.Unchecked_Conversion (Net_Packet, SNTP_Packet);
您可以使用pragma Default_Scalar_Storage_Order
,但是我不确定需要进行Bit_Order
匹配会怎样。
或者,如果您希望能够使用例如Float'Write
,您可以更改GNAT流基本类型的方式。
Ada运行时使用System.Stream_Attributes
,s-stratt.ads
文件中的软件包s-stratt.adb
处理基本类型的流,并在s-stratt__xdr.adb
中提供替代实现(在最新的编译器中) ;较早的编译器可能使用不同的文件名,但其中会有xdr
。
让编译器使用此替代版本不是很简单,但这对我有用:
将s-stratt__xdr.adb
复制到工作目录中的s-stratt.adb
使用gnatmake -a
在本地编译运行时的必要部分(-gnatpg
说“为运行时编译”):
gnatmake -a -f s-stratt.adb -gnatpg
构建程序:
gprbuild main.adb
请注意,gprbuild
不支持-a
。可能可以使用项目文件来创建包含修改后的运行时组件的库。
答案 2 :(得分:0)
您试图独立于热字节序以bigendian编码数据(可能用于网络传输)。
您期望UNCHECKED_CONVERSION
的两个参数都是Scalar_Storage_Order
= System.High_Order_First
定义的here
如果指定了相反的存储顺序,则每当读取类型为S的对象的标量分量的值时,都会首先反转封装机标量的存储元素。
您的问题来自使用旧的gcc版本。
由于Scalar_Storage_Order
使用以下代码,我试图通过测试FLOAT_T。System.Default_Bit_Order
从System.High_Order_First
到UNCHECKED_CONVERSION
的转换来分解问题。
inc.ads:
with SYSTEM;
package inc is
type BITS32_T is mod (2 ** 32);
for BITS32_T'SIZE use 32;
subtype UINT32_T is BITS32_T;
subtype INT32_UNSIGNED_T is UINT32_T;
type SIZE_T is new UINT32_T;
subtype INDEX_T is SIZE_T range 1 .. SIZE_T'LAST;
type BIT_T is mod (2 ** 1);
for BIT_T'SIZE use 1;
type BITS8_T is mod (2 ** 8);
for BITS8_T'SIZE use 8;
-- 64-bit signed integer
type INT64_T is range -(2 ** (64 - 1)) .. (2 ** (64 - 1) - 1);
for INT64_T'SIZE use 64;
subtype INT64_SIGNED_T is INT64_T;
type BIT_FIELD_ARR_NC_T is array (INDEX_T range <>) of BIT_T;
for BIT_FIELD_ARR_NC_T'COMPONENT_SIZE use 1;
for BIT_FIELD_ARR_NC_T'Scalar_Storage_Order use System.High_Order_First;
--Low_Order_First
type BYTE_FIELD_ARR_HOST_ENDIANNESS_NC_T is array (INDEX_T range <>) of BITS8_T;
for BYTE_FIELD_ARR_HOST_ENDIANNESS_NC_T'COMPONENT_SIZE use 8;
type BIT_FIELD_ARR_HOST_ENDIANNESS_NC_T is array (INDEX_T range <>) of BIT_T;
for BIT_FIELD_ARR_HOST_ENDIANNESS_NC_T'COMPONENT_SIZE use 1;
end inc;
test_types.adb:
with inc;
with INTERFACES;
with Ada.Text_IO;
with Ada.Integer_Text_IO;
with UNCHECKED_CONVERSION;
procedure TEST_TYPES is
longfloat : INTERFACES.IEEE_FLOAT_64 := INTERFACES.IEEE_FLOAT_64(1e11);
--float64 : inc.INT64_T := 16#1122334455667788#;
int64 : inc.INT64_T := 16#1122334455667788#;
---------------- TYPE used to print represnentation in memory ------------------------------
subtype BYTES_ARRAY_T is inc.BYTE_FIELD_ARR_HOST_ENDIANNESS_NC_T (1 .. 8);
-------- tableau de bits -------
subtype BITS_FIELD_T is inc.BIT_FIELD_ARR_NC_T (1 .. 64);
subtype BITS_FIELD_HOST_ENDIANNESS_T is inc.BIT_FIELD_ARR_HOST_ENDIANNESS_NC_T (1 .. 64);
---------------- FLOAT with BIG ENDIAN encoding ------------------------------
type TMP_TYPE_T is new STANDARD.LONG_FLOAT;
for TMP_TYPE_T'VALUE_SIZE use 64;
TMP_TYPE : TMP_TYPE_T;
function TO_BYTES_ARRAY is new UNCHECKED_CONVERSION (TMP_TYPE_T, BITS_FIELD_T);
bytes: BITS_FIELD_T;
---------------- FLOAT with host ENDIANNESS ------------------------------
function TO_BYTES_HOST_ENDIANNESS_ARRAY is new UNCHECKED_CONVERSION (TMP_TYPE_T, BITS_FIELD_HOST_ENDIANNESS_T);
bytesNoEndian: BITS_FIELD_HOST_ENDIANNESS_T;
---------------- INTEGER with ENDIAN CONVERSION ------------------------------
type TMP_Integer_T is new STANDARD.LONG_LONG_INTEGER;
for TMP_Integer_T'VALUE_SIZE use 64;
TMP_Integer : TMP_Integer_T;
function TO_BYTES_ARRAY_Integer is new UNCHECKED_CONVERSION (TMP_Integer_T, BITS_FIELD_T);
bytes_integer: BITS_FIELD_T;
---------------- INTEGER without ENDIAN CONVERSION ------------------------------
function TO_BYTES_ARRAY_HOST_ENDIANNESS_Integer is new UNCHECKED_CONVERSION (TMP_Integer_T, BITS_FIELD_HOST_ENDIANNESS_T);
bytes_no_endian_integer: BITS_FIELD_HOST_ENDIANNESS_T;
-- representation in memory
float_rep: BYTES_ARRAY_T;
float_bits_field_rep: BYTES_ARRAY_T;
int_rep: BYTES_ARRAY_T;
int_bits_field_rep: BYTES_ARRAY_T;
for float_rep'ADDRESS use bytesNoEndian'ADDRESS;
for float_bits_field_rep'ADDRESS use bytes'ADDRESS;
for int_rep'ADDRESS use bytes_no_endian_integer'ADDRESS;
for int_bits_field_rep'ADDRESS use bytes_integer'ADDRESS;
------------------ FUNCTION FROM STACKOVERFLOW-----------------------
function byte2hexa(byte : Integer) return String is
Hexa : String(1..8);
begin
Ada.Integer_Text_IO.Put(Hexa, byte, 16);
return Hexa;
end byte2hexa;
procedure array2hexa(bytes : BYTES_ARRAY_T) is
begin
Ada.Integer_Text_IO.Put(Integer(bytes(1)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(2)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(3)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(4)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(5)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(6)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(7)), Base => 16);
Ada.Integer_Text_IO.Put(Integer(bytes(8)), Base => 16);
Ada.Text_IO.New_line;
end array2hexa;
begin
-- test serialisation on float
TMP_TYPE := TMP_TYPE_T(longfloat);
bytesNoEndian := TO_BYTES_HOST_ENDIANNESS_ARRAY(TMP_TYPE);
Ada.Text_IO.Put_line("float in native endianess ");
array2hexa(float_rep);
Ada.Text_IO.New_line;
Ada.Text_IO.Put_line("float into BigEndian Bit array");
TMP_TYPE := TMP_TYPE_T(longfloat);
bytes := TO_BYTES_ARRAY(TMP_TYPE);
array2hexa(float_bits_field_rep);
Ada.Text_IO.New_line;
-- test serialisation on integer
TMP_Integer := TMP_Integer_T(int64);
bytes_no_endian_integer := TO_BYTES_ARRAY_HOST_ENDIANNESS_Integer(TMP_Integer);
Ada.Text_IO.Put_line("Integer in native endianess ");
array2hexa(int_rep);
Ada.Text_IO.New_line;
Ada.Text_IO.Put_line("Integer into BigEndian Bit array");
TMP_Integer := TMP_Integer_T(int64);
bytes_integer := TO_BYTES_ARRAY_Integer(TMP_Integer);
array2hexa(int_bits_field_rep);
end TEST_TYPES;
在我建议的代码中,问题来自于明确定义了BITS_FIELD_T'element的字节序,但未定义UNCHECKED_CONVERSION
的行为(关于从Float字节序向BITS_FIELD_T字节序的转换)
令人惊讶的是,使用gcc(GCC)6.2.1 20161010(对于GNAT Pro 17.2 20170606)
UNCHECKED_CONVERSION
转换整数的尾数而不是浮点数:
float in native endianess
16#0# 16#0# 16#0# 16#E8# 16#76# 16#48# 16#37# 16#42#
float into BigEndian Bit array
16#0# 16#0# 16#0# 16#E8# 16#76# 16#48# 16#37# 16#42#
Integer in native endianess
16#88# 16#77# 16#66# 16#55# 16#44# 16#33# 16#22# 16#11#
Integer into BigEndian Bit array
16#11# 16#22# 16#33# 16#44# 16#55# 16#66# 16#77# 16#88#
但带有gcc(GCC)7.3.1 20181018(适用于GNAT Pro 20.0w 20181017) 浮点值被核心交换:
float in native endianess
16#0# 16#0# 16#0# 16#E8# 16#76# 16#48# 16#37# 16#42#
float into BigEndian Bit array
16#42# 16#37# 16#48# 16#76# 16#E8# 16#0# 16#0# 16#0#
一种解决方案(适用于旧编译器)是在UNCHECKED_CONVERSION
之前通过中间的BigEndian结构:
procedure WRITE_LONG_FLOAT_T
(MY_STREAM : in out STREAMS.STREAM_T;
ITEM : in BASIC_TYPES.LONG_FLOAT_T;
ALIGN_MODE : in STREAMS.ALIGN_MODE_T)
is
pragma UNREFERENCED (ALIGN_MODE);
-- Temporary types for non pack case
type TMP_TYPE_T is new STANDARD.LONG_FLOAT;
for TMP_TYPE_T'VALUE_SIZE use LONG_FLOAT_T_SIZE_C;
TMP_TYPE : TMP_TYPE_T;
subtype BITS_FIELD_T is STREAMS.BIT_FIELD_ARR_NC_T (1 .. STREAMS.SIZE_T (LONG_FLOAT_T_SIZE_C));
function TO_BITS_ARRAY is new UNCHECKED_CONVERSION (TMP_TYPE_T,
BITS_FIELD_T);
type ITEM_ENDIAN_T is
record
TMP_TYPE_ENDIAN : TMP_TYPE_T;
end record;
for ITEM_ENDIAN_T'Bit_Order use System.High_Order_First;
for ITEM_ENDIAN_T'Scalar_Storage_Order use System.High_Order_First;
ITEM_ENDIAN : ITEM_ENDIAN_T;
--subtype LONG_FLOAT_TO_ARRAY_T is PUS.TYPES.BYTE_FIELD_NC_T (1 .. 8);
function TO_BITS_ARRAY_ENDIAN is new UNCHECKED_CONVERSION (ITEM_ENDIAN_T, BITS_FIELD_T);
begin
-- Convert item to a temporary type
TMP_TYPE := TMP_TYPE_T(ITEM);
ITEM_ENDIAN.TMP_TYPE_ENDIAN := TMP_TYPE;
STREAMS.WRITE (MY_STREAM => MY_STREAM,
--DATA => TO_BITS_ARRAY(TMP_TYPE));
DATA => TO_BITS_ARRAY_`enter code here`ENDIAN(ITEM_ENDIAN));
end WRITE_LONG_FLOAT_T;
答案 3 :(得分:0)
一种方法(或另一种思路...)是使用IEEE 754表示法包:http://www.dmitry-kazakov.de/ada/components.htm#IEEE_754
在这里完成了这些软件包的字节序中立使用:http://excel-writer.sf.net/