惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
MyScale Blog
MyScale Blog
Microsoft Security Blog
Microsoft Security Blog
量子位
B
Blog
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
T
The Exploit Database - CXSecurity.com
N
News | PayPal Newsroom
Cloudbric
Cloudbric
A
About on SuperTechFans
AI
AI
Hacker News: Ask HN
Hacker News: Ask HN
S
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 最新话题
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
有赞技术团队
有赞技术团队
H
Heimdal Security Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
D
Docker
Security Archives - TechRepublic
Security Archives - TechRepublic
N
News and Events Feed by Topic
IT之家
IT之家
Know Your Adversary
Know Your Adversary
N
Netflix TechBlog - Medium
T
Tailwind CSS Blog
B
Blog RSS Feed
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
博客园 - 叶小钗
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
The Hacker News
The Hacker News
Y
Y Combinator Blog
I
Intezer
The Register - Security
The Register - Security
F
Full Disclosure
V
V2EX
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler

博客园 - 有点懒惰的大青年

用一个实际业务场景演示ON和WHERE的正确使用方式 idea中查看源码 异或运算 使用Hutool对时间的花式操作 合并K个升序列表 比较器 枚举类的设计模式 spring boot中使用RedissonClient实现分布式锁 idea同时启动application,启用不同端口 @PostConstruct用法 ApplicationContext 事件发布与监听机制详解 java自带命令jps介绍 关于maven中标签的说明 mysql的跨库查询 linux命令ll显示结果的含义 Files类的使用 java环境变量设置 java值传递和引用传递 kafka的集群与可靠性 kafka的消费全流程 关于kafka 达梦数据库执行计划介绍 关于MQ
用位运算实现加减乘除(3)
有点懒惰的大青年 · 2025-05-11 · via 博客园 - 有点懒惰的大青年

用位运算实现加减乘除(2) 中的div方法要求a和b都能转成正数,但是:

系统最小值没法转成绝对值:Integer.MIN_VALUE

所有整数中只有这个数,是转不出绝对值的。

int a = Integer.MIN_VALUE;

System.out.println(-a);

所以分4钟情况讨论:

重点解析第3中情况:

假设-10就是系统最小,9是系统最大。我们没法把-10/2 转成 10/2,因为系统没有正10,系统最大值只到9.

那怎么算-10/2呢?

-10 先加1 得到-9,将-9/2得到-4。将-2*4 = -8,将-8和-10去比差了多少。-10 - (-8) = -2,差了-2。再将-2/2 = -1,加上原来的-4,得到-5.

其实很好理解:-4*3 + (-x) * 3 = -15

x = (15 - 4*3)/ 3

x = 1

所以就是-4 + -1 = -5

代码实现:

package com.cy.class05;

/**
 * 位运算实现+-*\\/
 * 测试连接:https://leetcode.com/problems/divide-two-integers
 */
public class Code03_BitAddMinusMultiDiv {

    public static int add(int a, int b) {
        int sum = a;
        while(b != 0) {
            sum = a ^ b;            //无进位相加信息   ->  sum
            b = (a & b) << 1;       //进位信息  -> b   ->  b'
            a = sum;                //无进位相加信息a  ->  a'
        }
        return sum;
    }

    /**
     * 返回n的相反数
     * 相反数 = 取反 + 1
     * -n = ~n + 1
     */
    public static int negNum(int n) {
        return add(~n, 1);
    }

    public static int minus(int a, int b){
        return add(a, negNum(b));
    }

    /**
     * 乘法
     *
     * 右移:
     * b >> 1,表示右移1,最高位拿符号位来补。
     * b >>>1, 表示右移1,最高位一律拿0来补。
     *
     * 支持负数,道理不好解释,负数的情况下到底发生了什么,跟补码有关,你只要知道同样这套方法既支持正数也支持负数。
     */
    public static int multi(int a, int b) {
        int res = 0;
        while(b != 0) {
            if ((b & 1) != 0) {
                res = add(res, a);
            }
            a <<= 1;
            b >>>= 1;
        }
        return res;
    }

    public static boolean isNeg(int n) {
        return n < 0;
    }

    /**
     * 除法
     *
     * 这个方法在正式开始之前,a和b一定要都转成正数
     *
     * x是非负的,第32位是符号位,肯定是0,所以没必要右移31位。所以for循环i从30开始。
     *
     * 这里^的运算是:非短路逻辑异或,两边的表达式都会执行
     * boolean a = true, b = false;
     * boolean c = a ^ b; // true(不同时为 true/false)
     * 如果需要逻辑异或(对布尔没必要值),直接用 != 更直观: boolean xor = (a != b); // 等效于 a ^ b
     *
     * a ^ b: a和b不同返回true,否则返回false
     *
     * 这个除法必须要求x和y转成正数的形式。
     * 这个方法一定是要能把a和b转成正数才能继续算,但系统最小值没办法转绝对值,所以有局限。
     */
    public static int div(int a, int b) {
        int x = isNeg(a) ? negNum(a) : a;
        int y = isNeg(b) ? negNum(b) : b;
        int res = 0;
        // x / y
        for (int i=30; i>=0; i = minus(i, 1)) {
            if ((x >> i) >= y) {
                res |= (1 << i);
                x = minus(x, y << i);
            }
        }
        //如果a和b符号不一样,取个负号返回;如果a和b负号一样,直接返回答案
        return isNeg(a) ^ isNeg(b) ? negNum(res) : res;
    }

    /**
     * 如果a和b都为系统最小,a = b,返回1
     * 如果b是系统最小,a不是,a / b,因为a的绝对值比b小,一除,向下取整,为0
     * 如果a是系统最小,b不是,将ans = (a + 1) / b,再(a - ans * b) / b 得到结果,再加ans。详细推演过程看草图。
     * 如果a和b都不是系统最小,直接采用上面写好的div
     *
     * 系统最小 ÷ -1,按道理应该是系统最大值+1的,但是这个值没办法表示,越界了。
     * leetcode约定,这时返回最大值就行了。
     *
     * @param a
     * @param b
     * @return
     */
    public static int divide(int a, int b) {
        if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
            return 1;
        } else if (b == Integer.MIN_VALUE) {
            return 0;
        } else if (a == Integer.MIN_VALUE) {
            if (b == negNum(1)) {
                return Integer.MAX_VALUE;
            } else {
                int ans = div(add(a, 1), b);
                return add(ans, div(minus(a, multi(ans, b)), b));
            }
        } else {
            return div(a, b);
        }
    }

    public static void main(String[] args) {
        int a = 7;
        int b = -3;
        System.out.println(multi(a, b));
    }

}

--