【ARMv8 SIMD和浮点指令编程】NEON 减法指令——减法也好几种

news2024/11/29 7:50:22

向量减法包括常见的普通加指令,还包括长减、宽减、半减、饱和减、按对减、按对加并累加、选择高半部分结果加、全部元素加等。

1 SUB

减法(向量),该指令从第一个源 SIMD&FP 寄存器中的相应向量元素中减去第二个源 SIMD&FP 寄存器中的每个向量元素,将结果放入一个向量中,并将该向量写入目标 SIMD&FP 寄存器。

标量

在这里插入图片描述

SUB <V><d>, <V><n>, <V><m>

向量

在这里插入图片描述

SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

<V> 是宽度说明符,以“size”编码:

size<V>
0xRESERVED
10RESERVED
11D

<d> 是 SIMD&FP 目标寄存器的编号,在“Rd”字段中。

<n> 是第一个 SIMD&FP 源寄存器的编号,编码在“Rn”字段中。

<m> 是第二个 SIMD&FP 源寄存器的编号,编码在“Rm”字段中。

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<T> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
110RESERVED
1112D

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 SUB 指令的例子。

    auto *srcArr = new int[4];

    for (int i = 0; i < 4; i++) {
        srcArr[i] = 100 * (i + 1);
    }

    char *src = (char *) srcArr;
    LOGD("in srcArr: %d %d %d %d", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);

    asm volatile(
        "LD1 {v0.4S}, [%[src]]\n"
        "SUB v1.4S, v0.4S, v0.4S\n"
        "ST1 {v1.4S}, [%[src]]\n"
    :[src] "+r"(src)
    :
    : "cc", "memory", "v0");

    LOGD("-----------------------------");
    LOGD("out srcArr: %d %d %d %d", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);

    delete[] srcArr;

SUB v1.4S, v0.4S, v0.4S 将 v0 寄存器 S 通道的值减去 v0 寄存器 S 通道的值的结果写入 v1 的 S 通道,不难知道最终结果均为 0。

运行结果:

2023-05-25 07:42:14.624 13572-13639/com.demo.myapplication D/NativeCore: in srcArr: 100 200 300 400
2023-05-25 07:42:14.624 13572-13639/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-25 07:42:14.624 13572-13639/com.demo.myapplication D/NativeCore: out srcArr: 0 0 0 0

2 SUBHN/SUBHN2

减法返回高窄,该指令从第一个源 SIMD&FP 寄存器中的相应向量元素中减去第二个源 SIMD&FP 寄存器中的每个向量元素,将结果的最高有效一半放入向量中,并将该向量写入目标 SIMD&FP 寄存器的下半部分或上半部分。 该指令中的所有值都是有符号整数值。

结果被截断。有关四舍五入的结果,请使用 RSUBHN。

SUBHN 指令将向量写入目标寄存器的下半部分并清除上半部分,而 SUBHN2 指令将向量写入目标寄存器的上半部分而不影响寄存器的其他位。

在这里插入图片描述

SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

下面是使用 SUBHN/SUBHN2 指令的例子。

    auto *srcArr = new unsigned long long int[4];
    auto *outArr = new unsigned int[4]{0};

    for (int i = 0; i < 4; i++) {
        srcArr[i] = 0x10010000000 * (i + 1);
    }

    char *src = (char *) srcArr;
    char *dst = (char *) outArr;
    LOGD("in srcArr: 0x%llx 0x%llx 0x%llx 0x%llx", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);
    LOGD("in outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);

    asm volatile(
        "LD1 {v0.2D, v1.2D}, [%[src]]\n"
        "SUBHN v2.2S, v1.2D, v0.2D\n"
        "SUBHN2 v2.4S, v0.2D, v1.2D\n"
        "ST1 {v2.4S}, [%[dst]]\n"
    :[src] "+r"(src),
    [dst] "+r"(dst)
    :
    : "cc", "memory", "v0", "v1", "v2");

    LOGD("-----------------------------");
    LOGD("out outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);

    delete[] srcArr;
    delete[] outArr;

SUBHN v2.2S, v1.2D, v0.2D 将 v1 的两个 D 通道的值减去 v0 的两个 D 通道的值,接着取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个低 S 通道,v2 寄存器高 S 通道全部清零。

SUBHN2 v2.4S, v0.2D, v1.2D 将 v0 的两个 D 通道的值减去 v1 的两个 D 通道的值,接着取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个高 S 通道,并保持 v2 其它位不变。

0x10010000000 - 0x30030000000 = 0x20020000000 - 0x40040000000 = 0xFFFF FDFF E000 0000,截断的方式取高位即 0xFFFF FDFF。

在这里插入图片描述

运行结果:

2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: in srcArr: 0x10010000000 0x20020000000 0x30030000000 0x40040000000
2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: in outArr: 0x0 0x0 0x0 0x0
2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: out outArr: 0x200 0x200 0xfffffdff 0xfffffdff

3 USUBL/USUBL2

无符号长减,该指令从第一个源 SIMD&FP 寄存器的相应向量元素中减去第二个源 SIMD&FP 寄存器的下半部分或上半部分中的每个向量元素,将结果放入一个向量,并将该向量写入目标 SIMD&FP 寄存器。该指令中的所有值都是无符号整数值。目标向量元素的长度是源向量元素的两倍。

USUBL 指令从每个源寄存器的下半部分提取每个源向量。USUBL2 指令从每个源寄存器的上半部分提取每个源向量。

在这里插入图片描述

USUBL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 USUBL/USUBL2 指令的例子。

    auto *srcArr = new unsigned int[8];
    auto *outArr = new unsigned long long int[4]{0};

    for (int i = 0; i < 8; i++) {
        srcArr[i] = 100 * (i + 1);
    }

    char *src = (char *) srcArr;
    char *dst = (char *) outArr;
    LOGD("in srcArr: %u %u %u %u %u %u %u %u", srcArr[0], srcArr[1], srcArr[2], srcArr[3],
         srcArr[4], srcArr[5], srcArr[6], srcArr[7]);

    asm volatile(
        "LD1 {v0.4S, v1.4S}, [%[src]]\n"
        "USUBL v2.2D, v1.2S, v0.2S\n"
        "USUBL2 v3.2D, v1.4S, v0.4S\n"
    "ST1 {v2.2D, v3.2D}, [%[dst]]\n"
    :[src] "+r"(src),
    [dst] "+r"(dst)
    :
    : "cc", "memory", "v0", "v1", "v2", "v3");

    LOGD("-----------------------------");
    LOGD("out outArr: %llu %llu %llu %llu", outArr[0], outArr[1], outArr[2], outArr[3]);

    delete[] srcArr;
    delete[] outArr;

USUBL v2.2D, v1.2S, v0.2S 将 v1 寄存器低半部分两个 S 通道的值减去 v0 寄存器低半部分两个 S 通道的值,并将结果写入 v2 寄存器的两个 D 通道。

USUBL2 v3.2D, v1.4S, v0.4S 将 v1 寄存器高半部分两个 S 通道的值减去 v0 寄存器高半部分两个 S 通道的值,并将结果写入 v3 寄存器的两个 D 通道。

运行结果:

2023-05-26 07:56:51.966 28776-28866/com.demo.myapplication D/NativeCore: in srcArr: 100 200 300 400 500 600 700 800
2023-05-26 07:56:51.967 28776-28866/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-26 07:56:51.967 28776-28866/com.demo.myapplication D/NativeCore: out outArr: 400 400 400 400

4 USUBW/USUBW2

无符号宽减,该指令从第一个源 SIMD&FP 寄存器的下半部分或上半部分中的相应向量元素中减去第二个源 SIMD&FP 寄存器的每个向量元素,将结果放入一个向量中,并将该向量写入 SIMD&FP 目标寄存器。该指令中的所有值都是无符号整数值。目标寄存器和第一个源寄存器的向量元素是第二个源寄存器的向量元素的两倍长。

USUBW 指令从第一个源寄存器的下半部分提取向量元素。USUBW2 指令从第一个源寄存器的上半部分提取向量元素。

在这里插入图片描述

USUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

下面是使用 USUBW/USUBW2 指令的例子。

    auto *srcArr = new unsigned int[4];
    auto *outArr = new unsigned long long int[4]{0};

    for (int i = 0; i < 4; i++) {
        srcArr[i] = 100 * (i + 1);
        outArr[i] = 1000 * (i + 1);
    }

    char *src = (char *) srcArr;
    char *dst = (char *) outArr;
    LOGD("in srcArr: %u %u %u %u", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);
    LOGD("in outArr: %llu %llu %llu %llu", outArr[0], outArr[1], outArr[2], outArr[3]);

    asm volatile(
        "LD1 {v0.4S}, [%[src]]\n"
        "LD1 {v1.2D, v2.2D}, [%[dst]]\n"
        "USUBW v3.2D, v1.2D, v0.2S\n"
        "USUBW2 v4.2D, v2.2D, v0.4S\n"
        "ST1 {v3.2D, v4.2D}, [%[dst]]\n"
    :[src] "+r"(src),
    [dst] "+r"(dst)
    :
    : "cc", "memory", "v0", "v1", "v2", "v3", "v4");

    LOGD("-----------------------------");
    LOGD("out outArr: %llu %llu %llu %llu", outArr[0], outArr[1], outArr[2], outArr[3]);

    delete[] srcArr;
    delete[] outArr;

USUBW v3.2D, v1.2D, v0.2S 将 v1 寄存器的两个 D 通道的值减去 v0 寄存器低半部分两个 S 通道的值 ,并将结果写入 v3 寄存器的两个 D 通道。

USUBW2 v4.2D, v2.2D, v0.4S 将 v2 寄存器的两个 D 通道的值减去 v0 寄存器高半部分两个 S 通道的值,并将结果写入 v4 寄存器的两个 D 通道。

在这里插入图片描述

运行结果:

2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: in srcArr: 100 200 300 400
2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: in outArr: 1000 2000 3000 4000
2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: out outArr: 900 1800 2700 3600

5 UQSUB

无符号饱和减法,该指令从第一个源 SIMD&FP 寄存器的相应元素值中减去第二个源 SIMD&FP 寄存器的元素值,将结果放入向量中,并将向量写入目标 SIMD&FP 寄存器。如果任何结果发生溢出,这些结果就会饱和。如果发生饱和,则设置累积饱和位 FPSR.QC。

标量

在这里插入图片描述

UQSUB <V><d>, <V><n>, <V><m>

向量

在这里插入图片描述

UQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

<V> 是宽度说明符,以“size”编码:

size<V>
00B
01H
10S
11D

<d> 是 SIMD&FP 目标寄存器的编号,在“Rd”字段中。

<n> 是第一个 SIMD&FP 源寄存器的编号,编码在“Rn”字段中。

<m> 是第二个 SIMD&FP 源寄存器的编号,编码在“Rm”字段中。

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<T> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
110RESERVED
1112D

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 UQSUB 指令的例子。

    auto *srcArr = new unsigned int[4];
    auto *outArr = new unsigned int[4];
    long long fpsrBefore = 0, fpsrAfter = 0;

    for (int i = 0; i < 4; i++) {
        srcArr[i] = 0xFFFFFFFF;
        outArr[i] = i;
    }

    char *src = (char *) srcArr;
    char *dst = (char *) outArr;
    LOGD("in srcArr: %u %u %u %u", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);
    LOGD("in outArr: %u %u %u %u", outArr[0], outArr[1], outArr[2], outArr[3]);

    asm volatile(
        "LD1 {v0.4S}, [%[src]]\n"
        "LD1 {v1.4S}, [%[dst]]\n"
        "MRS %[fpsrBefore], FPSR\n"
        "UQSUB v2.4S, v1.4S, v0.4S\n"
        "MRS %[fpsrAfter], FPSR\n"
        "ST1 {v2.4S}, [%[dst]]\n"
    :[src] "+r"(src),
     [dst] "+r"(dst),
     [fpsrBefore] "+r"(fpsrBefore),
     [fpsrAfter] "+r"(fpsrAfter)
    :
    : "cc", "memory", "v0", "v1", "v2");

    LOGD("-----------------------------");
    LOGD("out outArr: %u %u %u %u", outArr[0], outArr[1], outArr[2], outArr[3]);
    LOGD("out fpsrBefore: 0x%llx fpsrAfter: 0x%llx", fpsrBefore, fpsrAfter);

    delete[] srcArr;
    delete[] outArr;

UQSUB v2.4S, v1.4S, v0.4S 将 v1 寄存器的 S 通道的值减去 v0 寄存器 S 通道的值,由于 v1 减去 v0 不够减,可以看到 FPSR.QC(fpsrAfter 内,即运行 UQSUB 指令后的 FPSR 寄存器值的副本)已经被置为 1(从低到高第 27 位(低位从 0 开始))。

运行结果:

2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: in srcArr: 4294967295 4294967295 4294967295 4294967295
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: in outArr: 0 1 2 3
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: out outArr: 0 0 0 0
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: out fpsrBefore: 0x10 fpsrAfter: 0x8000010

6 UHSUB

无符号半减,该指令从第一个源 SIMD&FP 寄存器中的相应向量元素中减去第二个源 SIMD&FP 寄存器中的向量元素,将每个结果右移一位,将每个结果放入向量中,并将向量写入目标 SIMD&FP 寄存器。

在这里插入图片描述

UHSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<T> 是排列说明符,编码在“size:Q”字段中。它可以具有以下值:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 UHSUB 指令的例子。

    auto *srcArr = new unsigned int[4];
    auto *outArr = new unsigned int[4];

    for (int i = 0; i < 4; i++) {
        srcArr[i] = 0x01020304 * (i + 1);
        outArr[i] = 0x10203040 * (i + 1);
    }

    char *src = (char *) srcArr;
    char *dst = (char *) outArr;
    LOGD("in srcArr: 0x%x 0x%x 0x%x 0x%x", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);
    LOGD("in outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);

    asm volatile(
        "LD1 {v0.4S}, [%[src]]\n"
        "LD1 {v1.4S}, [%[dst]]\n"
        "UHSUB v2.4S, v1.4S, v0.4S\n"
        "ST1 {v2.4S}, [%[dst]]\n"
    :[src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory", "v0", "v1", "v2");

    LOGD("-----------------------------");
    LOGD("out outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);

    delete[] srcArr;
    delete[] outArr;

我们计算一组数值:0x10203040 - 0x1020304 = 0xF1E2D3C = 0b1111 00011110 00101101 00111100,右移一位后即 0b0111 10001111 00010110 10011110 = 0x78F169E。

运行结果:

2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: in srcArr: 0x1020304 0x2040608 0x306090c 0x4080c10
2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: in outArr: 0x10203040 0x20406080 0x306090c0 0x4080c100
2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: -----------------------------
2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: out outArr: 0x78f169e 0xf1e2d3c 0x16ad43da 0x1e3c5a78

7 RSUBHN/RSUBHN2

四舍五入减法返回高窄。该指令从第一个源 SIMD&FP 寄存器的相应向量元素中减去第二个源 SIMD&FP 寄存器的每个向量元素,将结果的最高有效一半放入向量,并将向量写入目标 SIMD&FP 寄存器的下半部分或上半部分。结果四舍五入。有关截断结果,请使用 SUBHN、SUBHN2。

RSUBHN 指令将向量写入目标寄存器的下半部分并清除上半部分,而 RSUBHN2 指令将向量写入目标寄存器的上半部分而不影响寄存器的其他位。

在这里插入图片描述

RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

下面是使用 RSUBHN/RSUBHN2 指令的例子。

    auto *srcArr = new unsigned long long int[4];
    auto *outArr = new unsigned int[4]{0};

    for (int i = 0; i < 4; i++) {
        srcArr[i] = 0x10010000000 * (i + 1);
    }

    char *src = (char *) srcArr;
    char *dst = (char *) outArr;
    LOGD("in srcArr: 0x%llx 0x%llx 0x%llx 0x%llx", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);
    LOGD("in outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);

    asm volatile(
        "LD1 {v0.2D, v1.2D}, [%[src]]\n"
        "RSUBHN v2.2S, v1.2D, v0.2D\n"
        "RSUBHN2 v2.4S, v0.2D, v1.2D\n"
        "ST1 {v2.4S}, [%[dst]]\n"
    :[src] "+r"(src),
    [dst] "+r"(dst)
    :
    : "cc", "memory", "v0", "v1", "v2");

    LOGD("-----------------------------");
    LOGD("out outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);

    delete[] srcArr;
    delete[] outArr;

RSUBHN v2.2S, v1.2D, v0.2D 将 v1 的两个 D 通道的值减去 v0 的两个 D 通道的值,接着四舍五入取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个低 S 通道,v2 寄存器高 S 通道全部清零。

RSUBHN2 v2.4S, v0.2D, v1.2D 将 v0 的两个 D 通道的值减去 v1 的两个 D 通道的值,接着四舍五入取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个高 S 通道,并保持 v2 其它位不变。

0x10010000000 - 0x30030000000 = 0x20020000000 - 0x40040000000 = 0xFFFF FDFF E000 0000,四舍五入的方式取高位即 0xFFFF FE00。

运行结果:

2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: in srcArr: 0x10010000000 0x20020000000 0x30030000000 0x40040000000
2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: in outArr: 0x0 0x0 0x0 0x0
2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: -----------------------------
2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: out outArr: 0x200 0x200 0xfffffe00 0xfffffe00

8 其他

SHSUB —— 有符号半减

SQSUB —— 有符号饱和减法

SSUBL/SSUBL2 —— 有符号长减

SSUBW/SSUBW2 —— 有符号宽减

参考资料

1.《ARMv8-A-Programmer-Guide》
2.《Arm® A64 Instruction Set Architecture Armv8, for Armv8-A architecture profile》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/607072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

旧改快讯--宝安又一项目计划失效:有效期内专规未获批

西井工业区城市更新单元计划&#xff08;失效&#xff09;公告 西井工业区城市更新单元原列入了《2019年深圳市宝安区城市更新单元五批计划》&#xff0c;更新单元计划有效期3年9个月&#xff0c;自2019年8月12日起至2023年5月11日止&#xff1b;该项目在计划有效期内更新单元规…

GAN培训挑战:用于彩色图像的DCGAN

GAN培训挑战&#xff1a;用于彩色图像的DCGAN 1. 效果图2. 原理2.1 用于彩色图像的 DCGAN2.3 准备数据2.4 生成器2.5 鉴别器2.6 DCGAN模型及回调函数2.7 GAN培训挑战2.8 未能聚合&#xff08;Non-convergence&#xff09;2.9 模式崩溃&#xff08;Mode collapse&#xff09;2.1…

chatgpt赋能python:Python取元素里的数字

Python取元素里的数字 Python是一种高级编程语言&#xff0c;它的使用范围非常广泛&#xff0c;在数据科学、人工智能、网络编程等领域都有很多应用。在Python程序中&#xff0c;我们经常需要从字符串或列表中提取数字&#xff0c;这篇文章将介绍在Python中如何取元素里的数字…

Anolis OS8 磁盘扩容

最近学习的时候&#xff0c;使用Vmware安装了AnolisOS8进行测试&#xff0c;随着学习的深入&#xff0c;组件安装越来越多&#xff0c;磁盘不够用了&#xff0c;但是安装的组件又太多&#xff0c;重新装个虚拟机又得重新装各种组件。所以决定对磁盘进行扩容&#xff0c;在这里做…

chatgpt赋能python:Python如何只提取文本中的数字?

Python如何只提取文本中的数字&#xff1f; 随着数字化时代的到来&#xff0c;数字成为了我们生活中不可或缺的一部分。我们每天都需要处理大量的数字&#xff0c;比如账单、统计数据等等&#xff0c;这些数字都散落在各个文本中。如果我们需要将这些数字提取出来&#xff0c;…

计算机视觉:目标检测理论及实战

有关锚框的部分可以看我的另一篇文章&#xff1a;点击这里。下文不再赘述 文章目录 多尺度目标检测多尺度锚框 数据集单发多框检测(SSD)模型设计类别预测层边界框预测层连结多尺度的预测高和宽减半块基本网络块完整的模型 训练导入数据集定义损失函数Utils函数(用于为每个锚框标…

【内网穿透】Linux本地搭建GitLab服务器

文章目录 前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar内网穿透5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名 7. 测试访问二级子域名 转载自cpolar极点云文章&#xff1a;Linux搭建GitLab私有仓库&#xff0c;并内网穿透实…

chatgpt赋能python:Python取款:让你的银行账户管理更智能

Python取款&#xff1a;让你的银行账户管理更智能 介绍 Python不仅是一种全球广泛应用的计算机编程语言&#xff0c;而且还拥有很多适合财务管理和数据处理的工具&#xff0c;用于提高效率和减少错误。本文将重点介绍如何使用Python自动管理银行账户的取款&#xff0c;以及它…

chatgpt赋能python:Python句点:为什么它如此重要?

Python句点&#xff1a;为什么它如此重要&#xff1f; 介绍 Python是一种高级编程语言&#xff0c;它以简单且易理解的语法而闻名。Python中有一种符号——句点&#xff08;.&#xff09;&#xff0c;它在Python中扮演着非常重要的作用。在本文中&#xff0c;我们将深入研究P…

chatgpt赋能python:Python只取小数

Python只取小数 Python是一种高级编程语言&#xff0c;被广泛应用于数据科学、人工智能、Web开发等领域。在数据分析和计算中&#xff0c;往往需要只保留小数&#xff0c;本文将介绍如何使用Python只取小数&#xff0c;并提供相关代码。 什么是小数? 在数学中&#xff0c;小…

Golang每日一练(leetDay0086) 回文链表、删除链表节点

目录 234. 回文链表 Palindrome Linked-list &#x1f31f; 237. 删除链表中的节点 Delete Node In a Linked-list &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练…

安卓逆向 -- Frida环境搭建(HOOK实例)

一、开启抓包程序Postern和Charles 二、目标分析 打开jadx&#xff0c;把apk拖拽进去&#xff0c;全局搜索"pwd"&#xff0c;挨个分析&#xff0c;明显来自于这条代码&#xff0c;后面是md5&#xff0c;可以判断pwd加密是md5&#xff0c;我们hook该地方 三、Frida环…

【23种设计模式】观察者模式(Observer Pattern)

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…

二叉树的链式结构 - C语言(含有大量递归)

目录&#xff1a; &#x1f354;前言 &#x1f354;二叉树链式结构的实现 &#x1f35f;基本构架 &#x1f60d;代码&#xff1a; &#x1f354;二叉树的遍历 &#x1f35f;前序遍历 &#x1f35f;中序遍历 &#x1f35f;后序遍历 &#x1f35f;层序遍历 &#x1f53…

chatgpt赋能python:Python快捷键:轻松运行你的代码

Python快捷键&#xff1a;轻松运行你的代码 Python是一种广泛使用的编程语言&#xff0c;因为它易于学习、易于使用&#xff0c;并提供了许多强大的库和框架。但是&#xff0c;在日常使用中经常需要重复与代码交互的操作&#xff0c;这可能会降低编程效率。使用Python快捷键可…

day43:1049. 最后一块石头的重量 II; 474. 一和零; 494.目标和:有多少种方式装满背包

01背包 [1049. 最后一块石头的重量 II (与416分割等和子集类似)](https://leetcode.cn/problems/last-stone-weight-ii/submissions/436837708/)1. dp数组以及下标名义2. 递归公式3. dp数组如何初始化4. 遍历顺序:从后往前遍历5. 代码 494.目标和:有多少种方式装满背包1. dp数组…

皮卡丘xss之htmlspecialchars、xss之href输出、xss之js输出

1.xss之htmlspecialchars htmlspecialchars()函数的功能如下&#xff1a; htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。 预定义的字符是&#xff1a; &#xff08;1&#xff09;& &#xff08;和号&#xff09;成为 &amp; &#xff08;2&#xff09;…

【编译、链接、装载三】编译器——语法分析、词法分析、语义分析、编译器后端

【编译和链接三】编译器——语法分析、词法分析、语义分析、编译器后端 内容总结一、词法分析&#xff08;Lexical Analysis&#xff09;二、语法分析 &#xff08;Syntactic Analysis, or Parsing&#xff09;三、语义分析&#xff08;Semantic Analysis&#xff09;四、编译器…

chatgpt赋能python:Python取出元素详解

Python取出元素详解 在Python编程中&#xff0c;常见到需要取出某个列表、元组或字典中的元素。本文将详细介绍Python如何取出这些元素&#xff0c;并提供相关代码和案例。 取出列表元素 列表是Python编程中最常见的数据结构&#xff0c;下面是列表的定义方式&#xff1a; …

chatgpt赋能python:Python程序的暂停使用介绍

Python程序的暂停使用介绍 Python是一种高级编程语言&#xff0c;适用于各种应用程序&#xff0c;包括Web开发、数据分析、机器学习等领域。它是一个非常强大的工具&#xff0c;但很多人可能不知道Python是否可以被暂停。在这篇文章中&#xff0c;我们将探讨Python是否可以暂停…